#include "DemoIDEPch.h"
#include "MainWindow.h"
#include "SM_Main.h"
#include "resource.h"
#include "MWinReg.h"
#include "Sm_DemoEffect.h"
#include "FXMusic.h"
#include "MSystemFunctions.h"
#include "MSurface.h"
#include "MLHelpers.h"
#include "ML.h"

using namespace SM_DemoScript;


 



int MainWindowLayout::Init(PreviewWindow* pPreview, TimelineWindow* pTimeline, WorksheetWindow* pWorksheet, ToolWindow* pTool, StatusWindow* pStatus)
{
  m_pPreview    = pPreview;
  m_pTimeline   = pTimeline;
  m_pWorksheet  = pWorksheet;
  m_pTool       = pTool;
  m_pStatus     = pStatus;
  
  return 0;
}

int MainWindowLayout::Shutdown()
{
  return 0;
}

void MainWindowLayout::Update()
{
  MainWindow* pMain = (MainWindow*) m_pWorksheet->Parent();
  
  int w,h;

  pMain->GetClientSize(&w, &h);

  int iPreviewSizeY = min((w-TITLEWIDTH)*3/4, 3*h/5);
  int iPreviewSizeX = min(w-TITLEWIDTH, iPreviewSizeY*4/3);
  

  m_pPreview->SetPosition(TITLEWIDTH, 0);
  
  m_pPreview->SetSize(iPreviewSizeX, iPreviewSizeY);
  m_pTimeline->SetPosition(TITLEWIDTH, iPreviewSizeY);
  m_pTimeline->SetSize(w - TITLEWIDTH, 32);
  m_pWorksheet->SetPosition(0, iPreviewSizeY+32);

  int arr[3];
  #define STATUSHEIGHT 20
  SendMessage(m_pStatus->Hwnd(), SB_GETBORDERS, 0, (LPARAM) arr);
  SendMessage(m_pStatus->Hwnd(), SB_SETMINHEIGHT, STATUSHEIGHT, 0);
  SendMessage(m_pStatus->Hwnd(), SB_SETMINHEIGHT, STATUSHEIGHT, 0);
  SendMessage(m_pStatus->Hwnd(), WM_SIZE, SIZE_RESTORED, HIWORD(h) | LOWORD(w));
  m_pStatus->UpdateWidth(w);

  m_pWorksheet->SetSize(w, h-iPreviewSizeY-32-STATUSHEIGHT-2*arr[0]);
  m_pTool->SetSize(TITLEWIDTH, iPreviewSizeY);
  //m_pStatus->SetPosition(0, h-10);
  //m_pStatus->SetSize(w, 10);

  
}

MainWindow::MainWindow()
{
  m_bActive         = false;
  m_bLoaded         = false;
  m_bDoPlayerUpdate = false;
  m_iTicks          = 1800;
  m_bPlaying        = false;
  m_iCurrentTick    = 0;
  m_bModified       = false;
  m_bNoMusic        = false;
  m_bNoMusicOnStop  = false;
  m_pAuxSurface     = 0;
  m_bBPMGuide       = false;
}

void MainWindow::Update()
{
  MusicFX::SetNoMusic(m_bNoMusic || (!m_bPlaying?m_bNoMusicOnStop:false));
  SetDemoTick(m_iCurrentTick);    
  if (m_bPlaying)
  {  
    PlayDemo();
  }  
}

int MainWindow::Init(Window* pParent)
{  
  if (InitPrivate("threepixels studio", 0, WS_OVERLAPPEDWINDOW, pParent) != 0)
  {
    return -1;
  }

  SetClassLong(m_hwnd, GCL_HICON, (LONG) LoadIcon(SM_Main::HInstance(), MAKEINTRESOURCE(IDI_MAIN))); 

  HMENU hmenu;
  if ( (hmenu = LoadMenu(SM_Main::HInstance(), MAKEINTRESOURCE(IDR_MAINMENU))) == NULL ||
       !SetMenu(m_hwnd, hmenu))
  {
    SM_Main::OutputError("Failed to load menu");
    return -1;
  }      

  // restore last window size, default to 10x7
  int x=0, y=0, w=1024, h=768;
  MWinreg::ReadInt("", "X", &x);
  MWinreg::ReadInt("", "Y", &y);
  MWinreg::ReadInt("", "Width", &w);
  MWinreg::ReadInt("", "Height", &h);
  SetPosition(x, y);
  SetSize(w, h);

  if (m_slGUILoadedEffects.Init() != 0)
  {
    return -1;
  }


  m_Timeline.Init(this);
  m_Worksheet.Init(this, &m_slGUILoadedEffects);
  m_Preview.Init(this);        
  m_Tool.Init(this);
  m_Status.Init(this);

  m_Timeline.AddListener(this);
  m_Worksheet.AddListener(this);
  m_Preview.AddListener(this);
  m_Tool.AddListener(this);


  m_pLayout = &m_Layout;

  if (m_Layout.Init(&m_Preview, &m_Timeline, &m_Worksheet, &m_Tool, &m_Status) != 0)
  {
    return -1;
  }

  m_Layout.Update();  
  
  Show();
  m_bIsOK     = true;
  m_bActive = true;
  return 0;
}

int MainWindow::Shutdown()
{
  ReleaseGUILoadedEffects();
  m_DemoPlayer.Shutdown();
  m_bLoaded = false;

  // remember window settings for next session
  int x=0, y=0, w=0, h=0;
  GetPosition(&x, &y);
  GetSize(&w, &h);
  
  MWinreg::WriteInt("", "X", x);
  MWinreg::WriteInt("", "Y", y);
  MWinreg::WriteInt("", "Width", w);
  MWinreg::WriteInt("", "Height", h);

  return 0;
}

int MainWindow::ProcessMessage  (const UINT message, const WPARAM wParam, const LPARAM lParam)
{

  switch (message)
  {
    case WM_SIZE:
      if (m_bActive)
      {
        m_Layout.Update();
      }
      break;
    case WM_CREATE:
      break;
    case WM_PAINT:
      break;
    case WM_CLOSE:
      if (!CheckModifySave())
      {
      	return 0;
      }
      Shutdown();
      break;
		case WM_DESTROY:
      if (m_bPlaying)
      {
        StopDemo();
      }

      PostQuitMessage(0);
      m_bActive = false;           
      break;
    case WM_COMMAND:
      int iMenu = wParam & 0xFFFF;
      switch (wParam)
      {
      case ID_FILE_OPENDEMO:
      	// If we haven't save, ask user      	
	      if (LoadDemo() != 0)
	      {
	        // We've failed to load.
	        SM_Main::OutputError("Failed to load demo\n");

					
        }      	
        break;
      }
  }

  return Window::ProcessMessage(message, wParam, lParam);
}

int MainWindow::LoadDemo()
{
  int iReturn = -1;
  if (CheckModifySave())
  {
    OPENFILENAME of;

    // Check for last succesfully opened script  
    char pcFileName[256];
    char pcUserFile[256];
    pcFileName[0] = '\0';
    pcUserFile[0] = '\0'; 

    memset(&of, 0, sizeof(of));
    of.lStructSize       = sizeof(of);
    of.hwndOwner         = this->Hwnd();
    of.hInstance         = SM_Main::HInstance();
    of.lpstrFilter       = "threepixels script (*.TXT)\0*.TXT\0\0";
    of.lpstrCustomFilter = 0;
    of.nMaxCustFilter    = 0;
    of.nFilterIndex      = 1;

    MWinreg::ReadString("", "LastOpened", pcFileName, sizeof(pcFileName)-1);
    
    of.lpstrFile         = pcFileName;
    of.nMaxFile          = sizeof(pcFileName)-1;
    of.lpstrFileTitle    = pcUserFile;
    of.nMaxFileTitle     = sizeof(pcUserFile)-1;
    of.lpstrInitialDir   = NULL;  
    of.lpstrTitle        = "Open Project";
    of.Flags             = OFN_HIDEREADONLY;
    of.nFileOffset       = 0;
    of.nFileExtension    = 0;
    of.lCustData         = 0;
    of.lpfnHook          = 0;
    of.lpstrDefExt       = 0;
    of.lpTemplateName    = 0;
    of.nFileOffset       = 0;


    if (!GetOpenFileName(&of))
    {
      return 0;
    }

    if (m_DemoPlayer.Init(m_Preview.Hwnd(), 640, 480, 32, true, false, false, false, false, true) != 0)
    {
      SM_Main::OutputError("Error initializing display");
      goto DOEXIT;
    }


    m_bLoaded = false;
    m_bPlaying = false;
    if (m_DemoPlayer.Load(of.lpstrFile) != 0)
    {
      goto DOEXIT;
    }

    if (ImportScript() != 0)
    {
      goto DOEXIT;
    }

    if (PreloadEffects() != 0)
    {
      goto DOEXIT;
    }

    m_bLoaded = true;
    m_bModified = false;

    m_Worksheet.SetActive(m_bLoaded);

    MWinreg::WriteString("", "LastOpened", of.lpstrFile);    

    LoadConfiguration(of.lpstrFile);

    iReturn = 0;
  }
  else
  {
    iReturn = 0;
  }
  

DOEXIT:
  if (iReturn == -1)
  {
    m_DemoPlayer.Shutdown();	  
  }

  UpdateCommandList();
	Update();

  return iReturn;;
}

int MainWindow::ShutdownCommandList(MBStaticList<SM_DemoScript::TCommand*>* pList)
{
  int iIterator;

  for (iIterator = pList->First() ; iIterator != -1 ; iIterator = pList->Next(iIterator))
  {
    SM_DemoScript::TCommand* pCommand;
    pList->Get(iIterator, pCommand);

    delete pCommand;
  }

  pList->Shutdown();

  return 0;
}

int MainWindow::GenerateCommandList(MBStaticList<SM_DemoScript::TCommand*>* pList, bool bAlsoInactive)
{
  int  iReturn = -1;
  int* piIterators = 0;
  int  iCompleted = 0;
  int  iCurrentTime = -1;
  int  i;
  SM_DemoScript::TCommand* pNewCommand = 0;
  
  // Shutdown current list
  ShutdownCommandList(pList);

  if (pList->Init() != 0)
  {
    return -1;
  }
  

  // Load Commands
  int iIterator;
  for (iIterator = m_slGUILoadedEffects.First() ; 
       iIterator != -1 ;
       iIterator = m_slGUILoadedEffects.Next(iIterator))
  {
    GUILoadedEffect* pLoadedEffect;
  
    m_slGUILoadedEffects.Get(iIterator, pLoadedEffect);
    if (pLoadedEffect->GetActive() || bAlsoInactive)
    {    
      SM_DemoScript::TCommand* pNewCommand = new TCommand;
      if (!pNewCommand)
      {
        goto FAILED;
      }

      pNewCommand->SetCommand(TCommand::CM_FX_LOAD);
      pNewCommand->SetFx(pLoadedEffect->DemoEffect());      
      pNewCommand->SetArgs(pLoadedEffect->LoadArguments());      

      if (pList->InsertTail(pNewCommand) == -1)
      {
        goto FAILED;
      }    
    }
  }

  pNewCommand = new TCommand;
  if (!pNewCommand)
  {
    goto FAILED;
  }

  pNewCommand->SetCommand(TCommand::CM_SYS_SYNCTIME);
  pNewCommand->SetTime(0.0f);
  if (pList->InsertTail(pNewCommand) == -1)
  {
    goto FAILED;
  }

  // Rest of commands
  piIterators = new int[m_slGUILoadedEffects.GetNumberElements()];
  if (!piIterators)
  {
    goto FAILED;
  }

  for (i = 0, iIterator = m_slGUILoadedEffects.First() ; 
       iIterator != -1 ;
       iIterator = m_slGUILoadedEffects.Next(iIterator), i++)
  {
    GUILoadedEffect* pLoadedEffect;  
    m_slGUILoadedEffects.Get(iIterator, pLoadedEffect);

    piIterators[i] = -1;

    // Only will load if its active
    if (pLoadedEffect->GetActive() || bAlsoInactive)
    {    
      piIterators[i] = pLoadedEffect->First();
    }
    
    if (piIterators[i] == -1)
    {
      iCompleted++;
    }
  }

  while (iCompleted < m_slGUILoadedEffects.GetNumberElements())
  {
    int iMinTime = 0x7FFFFFFF;
    int iMin = -1;
    int iMinIndex = -1;
    i = 0;

    // Select next command
    for (iIterator = m_slGUILoadedEffects.First() ; 
       iIterator != -1 ;
       iIterator = m_slGUILoadedEffects.Next(iIterator))
    {      
      if (piIterators[i] != -1)
      {
        GUILoadedEffect* pLoadedEffect;  
        m_slGUILoadedEffects.Get(iIterator, pLoadedEffect);

        GUICommand* pCommand;
        pLoadedEffect->Get(piIterators[i], pCommand);

        if (pCommand->m_iTick < iMinTime)
        {
          iMin = iIterator;
          iMinTime = pCommand->m_iTick;
          iMinIndex = i;
        }
      }

      i++;
    }

    assert(iMin != -1);
  
    // Update
    GUILoadedEffect* pLoadedEffect;  
    m_slGUILoadedEffects.Get(iMin, pLoadedEffect);

    // Output command
    GUICommand* pCommand;
    pLoadedEffect->Get(piIterators[iMinIndex], pCommand);

        
    if (pCommand->m_iTick != iCurrentTime)
    {
      float fTime = float(pCommand->m_iTick)/float(TICKSPERSECOND);

      pNewCommand = new TCommand;
      if (!pNewCommand)
      {
        goto FAILED;
      }

      pNewCommand->SetCommand(TCommand::CM_SYS_SYNCTIME);
      pNewCommand->SetTime(fTime);
      if (pList->InsertTail(pNewCommand) == -1)
      {
        goto FAILED;
      }
            
      // Update current time
      iCurrentTime = pCommand->m_iTick;
    }

    pNewCommand = pCommand->m_pCommand->Clone();
    if (!pNewCommand)
    {
      goto FAILED;
    }

    if (pList->InsertTail(pNewCommand) == -1)
    {
      goto FAILED;
    }
    
    // Fetch next index
    piIterators[iMinIndex] = pLoadedEffect->Next(piIterators[iMinIndex]);
    if (piIterators[iMinIndex] == -1)
    {
      iCompleted++;
    }

  }


  pNewCommand = 0;
  iReturn = max(0, iCurrentTime);

FAILED:
  if (piIterators) delete[] piIterators;
  if (pNewCommand) delete pNewCommand;  
  if (iReturn == -1) ShutdownCommandList(pList);

  return iReturn;
}

int MainWindow::UpdateCommandList()
{
  int iReturn = -1;

  // Shutdown current script 
  SM_DemoScript::ShutdownCommandList();

  MBStaticList<SM_DemoScript::TCommand*> List; 
  int iTicks;
  if ( (iTicks = GenerateCommandList(&List)) == -1)
  {
    goto FAILED;
  }
  
  int iIterator;

  for (iIterator = List.First() ; iIterator != -1 ; iIterator = List.Next(iIterator))
  {
    TCommand* pCommand;
    List.Get(iIterator, pCommand);
    
    SM_DemoScript::InsertCommandList(pCommand);    
  }

  List.Shutdown();

  // Update demo length
  SetLength(iTicks);


  iReturn = 0;

FAILED:

  return iReturn;
}

bool MainWindow::CheckModifySave()
{
  if (m_bModified)
  {
    int iResult = MessageBox(Hwnd(), "Do you want to save?", 
                                     "You have unsaved work", 
                                     MB_YESNOCANCEL | MB_ICONSTOP);

    if (iResult == IDYES)
    {
      if (SaveDemo() != 0)
      {
        SM_Main::OutputError("Failed to save demo\n");

				// Ooops. Failed to save				
        int iResult = MessageBox(Hwnd(), "Failed to save... do you want to discard changes?", 
                                     "You have unsaved work", 
                                     MB_YESNO | MB_ICONSTOP);

				return iResult == IDYES ? true:false;
      }
    }
    else if (iResult == IDCANCEL)
    {          
      return false;
    }
  }

	return true;
}

int MainWindow::ExportTGAs()
{
  int iFPS = 25;

  MSurface s;

  bool bOld = m_bNoMusicOnStop;

  m_bNoMusicOnStop = true;
  Update();
  StopDemo();
  

  m_pAuxSurface = &s;

  float fDemoTime  = float(m_iTicks)/float(TICKSPERSECOND);
  float fIncrement = 1.0f/ float(iFPS);

  
  int i=0;
  for (float fTime = 0.0f ; fTime < fDemoTime ; fTime += fIncrement)
  {
    SetDemoTick(int(fTime*TICKSPERSECOND));

    char pcName[256];
    sprintf(pcName, "TGA%05i.TGA", i++);
    s.SaveARGB32_TGA(pcName);
  }

  m_bNoMusicOnStop = false;
  Update();

  m_pAuxSurface = 0;
  
  return 0;
}

int MainWindow::SaveDemo()
{
  OPENFILENAME of;

  char pcFileName[256];
  char pcUserFile[256];
  pcFileName[0] = '\0';
  pcUserFile[0] = '\0'; 

  memset(&of, 0, sizeof(of));
  of.lStructSize       = sizeof(of);
  of.hwndOwner         = this->Hwnd();
  of.hInstance         = SM_Main::HInstance();
  of.lpstrFilter       = "threepixels script (*.TXT)\0*.TXT\0\0";
  of.lpstrCustomFilter = 0;
  of.nMaxCustFilter    = 0;
  of.nFilterIndex      = 1;
  
  MWinreg::ReadString("", "LastOpened", pcFileName, sizeof(pcFileName)-1);
  
  of.lpstrFile         = pcFileName;
  of.nMaxFile          = sizeof(pcFileName)-1;
  of.lpstrFileTitle    = pcUserFile;
  of.nMaxFileTitle     = sizeof(pcUserFile)-1;
  of.lpstrInitialDir   = NULL;
  of.lpstrTitle        = "Save Project";
  of.Flags             = OFN_HIDEREADONLY;
  of.nFileOffset       = 0;
  of.nFileExtension    = 0;
  of.lCustData         = 0;
  of.lpfnHook          = 0;
  of.lpstrDefExt       = 0;
  of.lpTemplateName    = 0;
  of.nFileOffset       = 0;

  if (!GetSaveFileName(&of))
  {
    return 0;
  }

  if (SaveDemo(of.lpstrFile) == -1)
  {
    return -1;
  }

  MWinreg::WriteString("", "LastOpened", of.lpstrFile);

  m_bModified = false;

  return 0;
}

int MainWindow::LoadConfiguration   (const char* pcScriptName)
{

  MetaFile ML;

  char pcConfigFile[256];

  strcpy(pcConfigFile, pcScriptName);
  strcat(pcConfigFile, ".cfg");
 

  if (ML.Load(pcConfigFile)!=0)
  {
    return (-1);
  }

  MetaFileNode* pRoot;

  pRoot=ML.Root()->GetSon("CONFIG");

  if (MetaFileNode* pBPMGuide = pRoot->GetSon("BPMGUIDE"))
  {
    if (pBPMGuide->GetSon("ON"))
    {
      m_bBPMGuide = true;
      m_Timeline.GetDemoIDEView()->m_bBPM = true;
      m_Tool.SetCheckBox(ToolWindow::E_CHK_BPMGUIDE, true);
    }
    else
    {
      m_Tool.SetCheckBox(ToolWindow::E_CHK_BPMGUIDE, false);
    }

    if (pBPMGuide->GetSon("BPM"))
    {
      m_Timeline.GetDemoIDEView()->m_iBPM = pBPMGuide->GetSon("BPM")->m_Sons[0]->m_Token.m_iValue;
    }

    if (pBPMGuide->GetSon("START"))
    {
      m_Timeline.GetDemoIDEView()->m_iStartBPM = pBPMGuide->GetSon("START")->m_Sons[0]->m_Token.m_iValue;
    }

    m_Timeline.Update();
  }

  if (pRoot->GetSon("NOMUSIC"))
  {
    m_bNoMusic = true;
    m_Tool.SetCheckBox(ToolWindow::E_CHK_NOMUSIC, true);
  }
  else
  {
    m_bNoMusic = false;
    m_Tool.SetCheckBox(ToolWindow::E_CHK_NOMUSIC, false);
  }

  if (pRoot->GetSon("NOMUSICONSTOP"))
  {
    m_bNoMusicOnStop = true;
    m_Tool.SetCheckBox(ToolWindow::E_CHK_NOMUSICONSTOP, true);
  }
  else
  {
    m_bNoMusicOnStop = false;
    m_Tool.SetCheckBox(ToolWindow::E_CHK_NOMUSICONSTOP, false);
  }

  UpdateMusic(m_bNoMusic, m_bNoMusicOnStop);
  
  return 0;
}

int MainWindow::SaveConfiguration(const char* pcScriptName)
{
  int iReturn = -1;
  MVFSFILE* f = 0;
  
  char pcConfigFile[256];

  strcpy(pcConfigFile, pcScriptName);
  strcat(pcConfigFile, ".cfg");
 
  f = MVFS::fopen(pcConfigFile, "wt");
  if (!f)
  {
    goto FAILED;
  }

  MLHelpers::InitWrite(f);  
  MLHelpers::WriteTag(MLHelpers::NestingLevel(), "CONFIG", true);

  MLHelpers::WriteTag(MLHelpers::NestingLevel(), "BPMGUIDE", true);

  // on/off
  if (m_bBPMGuide)
  {
    MLHelpers::WriteTag(MLHelpers::NestingLevel(), "ON", true);
    MLHelpers::WriteTag(MLHelpers::NestingLevel(), "ON", false);
  }

  MLHelpers::WriteTag(MLHelpers::NestingLevel(), "START", true);
  MLHelpers::WriteRaw(MLHelpers::NestingLevel(), "%i\n", m_Timeline.GetDemoIDEView()->m_iStartBPM);
  MLHelpers::WriteTag(MLHelpers::NestingLevel(), "START", false);  
  // start

  // bpms
  MLHelpers::WriteTag(MLHelpers::NestingLevel(), "BPM", true);
  MLHelpers::WriteRaw(MLHelpers::NestingLevel(), "%i\n", m_Timeline.GetDemoIDEView()->m_iBPM);
  MLHelpers::WriteTag(MLHelpers::NestingLevel(), "BPM", false);  
  

  MLHelpers::WriteTag(MLHelpers::NestingLevel(), "BPMGUIDE", false);

  if (m_bNoMusic)
  {
    MLHelpers::WriteTag(MLHelpers::NestingLevel(), "NOMUSIC", true);
    MLHelpers::WriteTag(MLHelpers::NestingLevel(), "NOMUSIC", false);
  }

  if (m_bNoMusicOnStop)
  {
    MLHelpers::WriteTag(MLHelpers::NestingLevel(), "NOMUSICONSTOP", true);
    MLHelpers::WriteTag(MLHelpers::NestingLevel(), "NOMUSICONSTOP", false);
  }

  MLHelpers::WriteTag(MLHelpers::NestingLevel(), "CONFIG", false);  
	

  MVFS::fclose(f);

  iReturn = 0;
FAILED:
  return iReturn;
}

int MainWindow::SaveDemo(const char* pcScriptName)
{
  int iReturn = -1;
  MVFSFILE* f = 0;
  MBStaticList<SM_DemoScript::TCommand*> List; 
  bool bFirst = true;

  
  if (SaveConfiguration(pcScriptName) != 0)
  {
    goto FAILED;
  }
  

  f = MVFS::fopen(pcScriptName, "wt");
  if (!f)
  {
    goto FAILED;
  }

  MVFS::fprintf(f, "//----------------------------------------\n");
  MVFS::fprintf(f, "// Script generated by threepixels Studio\n");
  MVFS::fprintf(f, "//----------------------------------------\n");

  MVFS::fprintf(f, "\n");  
  MVFS::fprintf(f, "//----------------------------------------\n");
  MVFS::fprintf(f, "// Load Musics\n");
  MVFS::fprintf(f, "//----------------------------------------\n");

  
  
  if (GenerateCommandList(&List, true) == -1)
  {
    goto FAILED;
  }

  int iIterator;

  MVFS::fprintf(f, "\n");
  MVFS::fprintf(f, "//----------------------------------------\n");
  MVFS::fprintf(f, "// Load Effects\n");
  MVFS::fprintf(f, "//----------------------------------------\n");   

  int iNext;
  for (iIterator = List.First() ; iIterator != -1 ; iIterator = iNext)
  {
    iNext = List.Next(iIterator);

    SM_DemoScript::TCommand* pCommand;
    List.Get(iIterator, pCommand);

    if (pCommand->GetCommand() == TCommand::CM_FX_LOAD)
    {
      
      char pcString[1024];
      pCommand->ToString(pcString , 1024);

      MVFS::fprintf(f, "%s", pcString);
      MVFS::fprintf(f, "FXLAYER %s %i\n", pCommand->GetFxName(), pCommand->GetFx()->m_iLayer);

      delete pCommand;
      List.Delete(iIterator);
    }
  }  

  MVFS::fprintf(f, "\n");
  MVFS::fprintf(f, "//----------------------------------------\n");
  MVFS::fprintf(f, "// Script\n");
  MVFS::fprintf(f, "//----------------------------------------\n");  
  for (iIterator = List.First() ; iIterator != -1 ; iIterator = List.Next(iIterator))
  {
    SM_DemoScript::TCommand* pCommand;
    List.Get(iIterator, pCommand);

    if (!bFirst && pCommand->GetCommand() == TCommand::CM_SYS_SYNCTIME)
    {
      MVFS::fprintf(f, "\n");  
    }

    char pcString[1024];
    pCommand->ToString(pcString , 1024);
    MVFS::fprintf(f, "%s", pcString);

    bFirst = false;
  }

  
  ShutdownCommandList(&List);
      
  iReturn = 0;
  
FAILED:
  if (f) MVFS::fclose(f);

  return iReturn;
}


bool MainWindow::IsPlaying()
{
  return m_bPlaying;
}

int MainWindow::PreloadEffects()
{
  int iIterator;

  GUILoadedEffect* pGUILoadedEffect;

  for (iIterator = m_slGUILoadedEffects.First() ; 
       iIterator != -1 ; 
       iIterator = m_slGUILoadedEffects.Next(iIterator))
  {
    m_slGUILoadedEffects.Get(iIterator, pGUILoadedEffect);   

    if (pGUILoadedEffect->DemoEffect()->Init(pGUILoadedEffect->LoadArguments()) != 0)
    {
      SM_Main::OutputError("Failed to preload %s.!!!. Marking as inactive", pGUILoadedEffect->DemoEffect()->m_pcInstanceName);
      pGUILoadedEffect->SetActive(false);
    }
    else
    {
      pGUILoadedEffect->SetActive(true);
    }
  }

  return 0;
}

int MainWindow::ReleaseGUILoadedEffects()
{
  int iIterator;
  int iNext;

  GUILoadedEffect* pGUILoadedEffect;

  for (iIterator = m_slGUILoadedEffects.First() ; iIterator != -1 ; iIterator = iNext)
  {
    iNext = m_slGUILoadedEffects.Next(iIterator);
    m_slGUILoadedEffects.Get(iIterator, pGUILoadedEffect);

    pGUILoadedEffect->DemoEffect()->Shutdown();
    delete pGUILoadedEffect;

    m_slGUILoadedEffects.Delete(iIterator);
  }

  return 0;
}


int MainWindow::ImportScript()
{
  TCommandNode* pNode;
  int iReturn = -1;
  GUILoadedEffect* pEffect = 0;
  int iMaxTicks = 0;

  ReleaseGUILoadedEffects();
  
  for (pNode = GetCommandList() ; pNode ; pNode = pNode->pNext)
  {
    switch (pNode->pCommand->GetCommand())
    {
      case TCommand::CM_SYS_SYNCTIME:
        iMaxTicks = max(int(100.0*pNode->pCommand->GetTime()), iMaxTicks);
        break;            
      case TCommand::CM_SYS_SYNC:
        SM_Main::OutputError("Imported script has sync commands that aren't SYNCTIME. This is unsupported\n");
        break;
      case TCommand::CM_FX_LOAD:
      {
        // Check if the effect is already added.
        if (!FindGUIEffect(&m_slGUILoadedEffects, pNode->pCommand->GetFx()))
        {        
          pEffect = new GUILoadedEffect();
          if (!pEffect)
          {
            goto ISERROR;
          }
    
          if (pEffect->Init(pNode->pCommand->GetFx(), pNode->pCommand->GetArgs()) != 0)
          {
            goto ISERROR;
          }

          if (m_slGUILoadedEffects.InsertTail(pEffect) == -1)
          {
            goto ISERROR;
          }

          pEffect = 0;    
        }

        break;
      }    
      case TCommand::CM_FX_START:
      case TCommand::CM_FX_STOP:
      case TCommand::CM_FX_COMMAND:
      case TCommand::CM_FX_LAYER:
      {
        
        GUILoadedEffect* pGUIEffect;

        if (!(pGUIEffect = FindGUIEffect(&m_slGUILoadedEffects, pNode->pCommand->GetFx())))
        {
          SM_Main::OutputError("Can't import because commands are send %s before it's been loaded",
                      pNode->pCommand->GetFxName());
          goto ISERROR;
        }

        if (pNode->pCommand->GetCommand() == TCommand::CM_FX_LAYER)
        {
          // Adjust layer for effect          
          pGUIEffect->DemoEffect()->m_iLayer = atoi(pNode->pCommand->GetArgs());
        }
        else
        {        
          GUICommand* pCommand = new GUICommand();
          if (!pCommand)
          {
            goto ISERROR;
          }

          if (pCommand->Init(pNode->pCommand, iMaxTicks) != 0)
          {
            delete pCommand;
            goto ISERROR;
          }
          
          if (pGUIEffect->Insert(pCommand) == -1)
          {
            delete pCommand;
            goto ISERROR;
          }
        }
      }
    }

    /*
#ifdef _DEBUG
    {
      SM_Main::OutputConsole("Command dump\n");
      int iIterator;
      int iCommand;

      
      for (iIterator = m_slGUILoadedEffects.First() ; iIterator != -1 ; iIterator = m_slGUILoadedEffects.Next(iIterator))
      {
        m_slGUILoadedEffects.Get(iIterator, pEffect);

        for (iCommand = pEffect->First() ; iCommand != -1 ; iCommand = pEffect->Next(iCommand))
        {
          GUICommand* pCommand;
          pEffect->Get(iCommand, pCommand);

          char pcCommand[1024];
          pCommand->m_pCommand->ToString(pcCommand, 1024);
          SM_Main::OutputConsole("%s(%i) %s", pEffect->DemoEffect()->m_pcInstanceName, pCommand->m_iTick, pcCommand);
        }
      }

      
    }
#endif
    */

  }

#ifdef _DEBUG
  {
    int iIterator;
    for (iIterator = m_slGUILoadedEffects.First() ; iIterator != -1 ; iIterator = m_slGUILoadedEffects.Next(iIterator))
    {
      m_slGUILoadedEffects.Get(iIterator, pEffect);
      pEffect->TestIntegrity();
    }
    pEffect = 0;
  }
#endif

  // Update timeline control
  SetLength(iMaxTicks);

  // Update worksheet control
  m_Worksheet.UpdateWorksheet();

  InvalidateRect(m_hwnd, 0, FALSE);

  

  iReturn = 0;
  
ISERROR:
  if (pEffect) delete pEffect;

  return iReturn;
}

void MainWindow::SetLength(int iTicks)
{
  m_iTicks = iTicks;
  m_Timeline.SetLength(m_iTicks+500); // 500, so that an extra 'block' appears at the end of the timeline in the largest scale
}

void MainWindow::UpdateMusic(bool bNoMusic, bool bNoMusicOnStop)
{
  m_bNoMusic       = bNoMusic;
  m_bNoMusicOnStop = bNoMusicOnStop;
}

void MainWindow::ProcessEvent(Event* pEvent)
{  
  if (AreEqualEVGUID(pEvent->GUID(), &EV_TIMELINE_UPDATE))
  {
    ((TimelineUpdateEvent*) pEvent)->m_DemoIDEView.m_iPlaytick = ((TimelineUpdateEvent*) pEvent)->m_DemoIDEView.m_iCurrentTick;
    m_Worksheet.SetView(&((TimelineUpdateEvent*) pEvent)->m_DemoIDEView, true);
  }
  else
  if (AreEqualEVGUID(pEvent->GUID(), &EV_TIMESCALE_UPDATE))
  {
    TimescaleUpdateEvent* pTimescaleEvent = (TimescaleUpdateEvent*) pEvent;

    m_Timeline.SetScale(pTimescaleEvent->m_iScale);
  }     
  else
  if (AreEqualEVGUID(pEvent->GUID(), &EV_TOOLBUTTON_PRESSED))
  {
    ToolButtonPressedEvent* pButtonPressedEvent = (ToolButtonPressedEvent*) pEvent;
    
    switch (pButtonPressedEvent->m_Button)
    {
    case ToolButtonPressedEvent::E_NEWEFFECT:
        m_Worksheet.InsertEffect();
        break;
    case ToolButtonPressedEvent::E_LOAD:
        if (LoadDemo() != 0)
        {
          SM_Main::OutputError("Failed to load demo\n");
        }        
        break;

    case ToolButtonPressedEvent::E_SAVE:
        if (SaveDemo() != 0)
        {
          SM_Main::OutputError("Failed to save demo\n");
        }                
        break;
    case ToolButtonPressedEvent::E_EXPORTTGAS:
        if (MessageBox(Hwnd(), "Are you sure you want to export tgas? (it takes a while and can't abort). TODO: smarter dialog/logic", "Export TGAs", MB_YESNO | MB_ICONWARNING) == IDYES)
        {
          if (ExportTGAs() != 0)
          {
            SM_Main::OutputError("Failed to export TGAs\n");
          }                
        }
        break;
    case ToolButtonPressedEvent::E_PLAY:
        m_bPlaying = true;
        Update();
        break;
    case ToolButtonPressedEvent::E_STOP:
    case ToolButtonPressedEvent::E_PAUSE:
        if (m_bLoaded)
        {
          if (StopDemo() != 0)
          {
            SM_Main::OutputError("Failed to play demo\n");
          }                
        }
        Update();
        break;        
    }
    
  }       
  else
  if (AreEqualEVGUID(pEvent->GUID(), &EV_TIMESET))
  {
    SetDemoTick(((TimesetEvent*)pEvent)->m_DemoIDEView.m_iCurrentTick);
    Update();
  } 
  else
  if (AreEqualEVGUID(pEvent->GUID(), &EV_PREVIEW_PAINT))
  {
    SetDemoTick( m_iCurrentTick );    
  }
  else if (AreEqualEVGUID(pEvent->GUID(), &EV_WORKSHEETUPDATE))
  {    
    UpdateCommandList();
    Update();
  }
  else if (AreEqualEVGUID(pEvent->GUID(), &EV_DEBUGTEXT))
  {
    m_DemoPlayer.g_bDrawFPS=!m_DemoPlayer.g_bDrawFPS;    
    SetDemoTick(m_iCurrentTick);
  }
  else if (AreEqualEVGUID(pEvent->GUID(), &EV_BPMGUIDE))
  {
    m_bBPMGuide = ((BPMGuideEvent*) pEvent)->m_bBPMGuide;
    m_Timeline.SetBPMGuide(m_bBPMGuide);    
    SetDemoTick(m_iCurrentTick);
  }
  else if (AreEqualEVGUID(pEvent->GUID(), &EV_NOMUSIC))
  {
    UpdateMusic(((NoMusicEvent*) pEvent)->m_bNoMusic,
                ((NoMusicEvent*) pEvent)->m_bNoMusicOnStop);
    Update();
  }       
  else if (AreEqualEVGUID(pEvent->GUID(), &EV_WORKSHEETMARKEVENT))
  {
    m_Status.SetText(StatusWindow::E_DESCRIPTOR, ((WorksheetMarkEvent*) pEvent)->m_pcText);    
  }
  else if (AreEqualEVGUID(pEvent->GUID(), &EV_COORDUPDATE))
  {
    char pcCoords[128];

    sprintf(pcCoords, "%i : %i", ((CoordEvent*) pEvent)->m_x, ((CoordEvent*) pEvent)->m_y);
    m_Status.SetText(StatusWindow::E_DESCRIPTOR, pcCoords);    
  }
  else
  if (AreEqualEVGUID(pEvent->GUID(), &EV_WORKSHEETMODIFIEDEVENT))
  {
    m_bModified = true;
  }   
}

void MainWindow::SetDemoTick(int iTick)
{
  if (m_bLoaded)
  {
    m_iCurrentTick = iTick;
    float fTime = float(iTick)/100.0f;

    if (!m_bPlaying)
    {
      m_DemoPlayer.RunToTime(fTime, m_pAuxSurface);  
    }    
  }
}

void MainWindow::SetPlayingTick(int iTick)
{
  DemoIDEView View;

  // We get the current view and update the playing tick
  View = *m_Worksheet.GetView();  
  View.m_iPlaytick = iTick;
  m_Worksheet.SetView(&View, false);
}

int MainWindow::PlayDemo()
{
  //m_fStartPlayTime = timeGetTime()/1000.0f - ;
  SM_Main::OutputConsole("MainWindow::PlayDemo()\n");
  m_fStartPlayTime = timeGetTime()/1000.0f; 
  m_DemoPlayer.RunToTime(float(m_iCurrentTick)/100.0f);    
  m_bPlaying = true;
  return 0;
}


int MainWindow::StopDemo()
{
  m_bPlaying = false;
  return 0;
}

void MainWindow::Run()
{
  if (m_bLoaded)
  {
    if (m_bPlaying)
    {
      int iCurrentTick = int(((timeGetTime()/1000.0f-m_fStartPlayTime)*100.0f)) + m_iCurrentTick;
      m_DemoPlayer.Run(float(iCurrentTick)/100.0f);
      SetPlayingTick(iCurrentTick);
    }    
  } 
}
  
