#include "DemoIDEPch.h"
#include "WorksheetWindow.h"
#include "SM_DemoEffect.h"
#include "MainWindow.h" // @TODO: fixme. reorg data structures in another file
#include "resource.h"


using namespace SM_DemoScript;


WorksheetWindow::WorksheetWindow()
{
  Reset();
}

void WorksheetWindow::SetActive(bool bActive)
{
  m_bActive = bActive;
}

WorksheetWindow::~WorksheetWindow()
{
  Shutdown();  
}

int WorksheetWindow::PixelToTick(int x)
{
  return m_DemoIDEView.PixelToTick(x-TITLEWIDTH);  
}

int WorksheetWindow::TickToPixel(int iTick)
{
  return m_DemoIDEView.TickToPixel(iTick)+TITLEWIDTH;    
}


void WorksheetWindow::UpdateScrollBar()
{
  int h;
  GetClientSize(0, &h);
  m_iPageSize = h/ROWHEIGHT;

  // Set control bar
  SCROLLINFO si;
  si.cbSize = sizeof(SCROLLINFO);
  si.fMask  = SIF_RANGE;
  si.nMin   = 0;
  si.nMax   = m_pslEntryList->GetNumberElements()?m_pslEntryList->GetNumberElements() - 1:0;
  SetScrollInfo(m_hwnd, SB_VERT, &si, TRUE);

  si.fMask  = SIF_PAGE;
  si.nPage  = m_iPageSize;
  SetScrollInfo(m_hwnd, SB_VERT, &si, TRUE);
}

int WorksheetWindow::Init(Window* pParent, MBStaticList<GUILoadedEffect*>* pslEntryList)
{
  m_pslEntryList = pslEntryList;
  

  if (Window::InitPrivate("Worksheet", 0, WS_VSCROLL | WS_CHILDWINDOW, pParent) == -1)
  {
    return -1;
  }

  SetSize(640, 400);
  
  m_bIsOK = true;

  
  UpdateScrollBar();
  m_iEntryOffset = 0;

  // GDI Objects
  LOGFONT    lf = {-12,0,0,0,400,0,0,0,0,3,2,1,34,"Trebuchet MS"};  
  
  hBrushEven        = CreateSolidBrush(RGB(255, 240, 240));
  hBrushOdd         = CreateSolidBrush(RGB(255, 250, 250));
  hBrushBlack       = CreateSolidBrush(RGB(0, 0, 0));
  hBrushWhite       = CreateSolidBrush(RGB(255, 255, 255));
  hBrushGreen       = CreateSolidBrush(RGB(0, 255, 0));
  hBrushBPM         = CreateSolidBrush(RGB(100, 100, 224));
  hBrushRed         = CreateSolidBrush(RGB(255, 0, 0));
  hBrushHalfRed     = CreateSolidBrush(RGB(128, 0, 0));
  hBrushGrey        = CreateSolidBrush(RGB(255, 128, 128));
  hfont             = CreateFontIndirect (&lf);
  hLimitFill        = CreateSolidBrush(RGB(224, 224, 224));
  hLimitOutline     = CreatePen(PS_SOLID, 0, RGB(192, 192, 192));
  hBrushYellow      = CreateSolidBrush(RGB(224, 224, 0));
  hPenHalfYellow    = CreatePen(PS_SOLID, 0, RGB(162, 162, 0));


  Show();    
  return 0;
}

int WorksheetWindow::Shutdown()
{
  if (m_bIsOK)
  {
    DeleteObject(hBrushEven   );
    DeleteObject(hBrushOdd    );
    DeleteObject(hBrushBlack  );
    DeleteObject(hBrushWhite  );
    DeleteObject(hBrushGreen  );
    DeleteObject(hBrushBPM    );
    DeleteObject(hBrushRed    );
    DeleteObject(hBrushHalfRed);
    DeleteObject(hBrushGrey   );
    DeleteObject(hLimitFill   );
    DeleteObject(hLimitOutline);
    DeleteObject(hBrushYellow);
    DeleteObject(hPenHalfYellow);

    m_pslEntryList = 0;  
    m_bIsOK = false;
  }

  ShutdownWidgetList();      
  return 0;
}

int WorksheetWindow::GetWidgetFromCoord(int x, int y, int iMask)
{
  int     iIterator;
  sWidget* pWidget;

  for (iIterator = m_slWidgets.First() ; iIterator!=-1 ; iIterator = m_slWidgets.Next(iIterator))
  {
    m_slWidgets.Get(iIterator, pWidget);
    if (int(pWidget->eType) & iMask)
    {
      assert(pWidget->hRegion);
      if (PtInRegion(pWidget->hRegion, x, y))
      {
        return iIterator;
      }
    }
  }

  return -1;
}

void WorksheetWindow::UpdateWidgetTick(int iWidget, int iTick)
{
  sWidget*          pWidget;
  GUILoadedEffect*  pEffect;
  GUICommand*       pCommand;

  SM_Main::OutputConsole("Widget update: %i %i\n", iWidget, iTick);
  m_slWidgets.Get(iWidget, pWidget);
  m_pslEntryList->Get(pWidget->iGUIEffect, pEffect);
  pEffect->Get(pWidget->iCommand, pCommand);
  pCommand->m_iTick = iTick;

  int iIterator;
  if (pCommand->m_pCommand->GetCommand() == TCommand::CM_FX_START || 
      pCommand->m_pCommand->GetCommand() == TCommand::CM_FX_STOP)
  {
    // Find next limit command and make sure we don't go over it
    iIterator = pWidget->iCommand;
    for (iIterator = pEffect->Next(pWidget->iCommand) ; 
         iIterator != -1 ; 
         iIterator = pEffect->Next(iIterator))
    {
      GUICommand* pTempCommand;
      pEffect->Get(iIterator, pTempCommand);

      if ( (pTempCommand->m_pCommand->GetCommand() == TCommand::CM_FX_START  || 
            pTempCommand->m_pCommand->GetCommand() == TCommand::CM_FX_STOP)  &&
            pCommand->m_iTick >= pTempCommand->m_iTick)
      {
        pCommand->m_iTick = pTempCommand->m_iTick;
      }
    }

    // Find previous limit command and make sure we don't go over it
    iIterator = pWidget->iCommand;
    for (iIterator = pEffect->Previous(pWidget->iCommand) ; 
         iIterator != -1 ; 
         iIterator = pEffect->Previous(iIterator))
    {
      GUICommand* pTempCommand;
      pEffect->Get(iIterator, pTempCommand);

      if ( (pTempCommand->m_pCommand->GetCommand() == TCommand::CM_FX_START  || 
            pTempCommand->m_pCommand->GetCommand() == TCommand::CM_FX_STOP)  &&
            pCommand->m_iTick <= pTempCommand->m_iTick)
      {
        pCommand->m_iTick = pTempCommand->m_iTick;
      }
    }
  }
  
  // Reinsert command
  pEffect->Delete(pWidget->iCommand);
  pWidget->iCommand = pEffect->Insert(pCommand);
  
  
  assert(pWidget->iCommand != -1);
}


int WorksheetWindow::InsertCommand(GUILoadedEffect* pEffect, TCommand::eCommand eType, const char* pcArgs, int iTick)
{ 
  SM_DemoScript::TCommand Command;
  GUICommand* pGUICommand = 0;
  int iID = -1;
  
  if (Command.Init() != 0)
  {
    goto FAILURE;
  }

  Command.SetFx(pEffect->DemoEffect());
  Command.SetCommand(eType);
  if (pcArgs) Command.SetArgs(pcArgs);

  pGUICommand = new GUICommand;
  if (!pGUICommand)
  {
    goto FAILURE;
  }

  if (pGUICommand->Init(&Command, iTick) != 0)
  {
    goto FAILURE;
  }

  if ( (iID = pEffect->Insert(pGUICommand)) == -1)
  {
    goto FAILURE;
  }

  return iID;

FAILURE:
  delete pGUICommand;
  return -1;
}

void WorksheetWindow::Reset()
{
  m_pslEntryList                 = 0;
  m_iCurrentEffect               = 0;
  m_bActive                      = false;
  m_CopyPasteBuffer.m_bSomething = false;
  m_iCurrentEffect               = -1;
  m_bDrag                          = false;  
  m_bDragEffect                    = false;  
}

void WorksheetWindow::InsertRange(int iEffect)
{
  GUILoadedEffect* pGUILoadedEffect;
  m_pslEntryList->Get(m_iCurrentEffect, pGUILoadedEffect);

  if (pGUILoadedEffect->IsTickInActiveRange(m_iCurrentTick))
  {
    SM_Main::OutputError("Can't insert range inside another range");
    return;
  }    

  int iStart;
  if ( (iStart = InsertCommand(pGUILoadedEffect, TCommand::CM_FX_START, 0, m_iCurrentTick)) == -1)
  {
    return;
  }

  if ( (InsertCommand(pGUILoadedEffect, TCommand::CM_FX_STOP, 0, m_iCurrentTick)) == -1)
  {
    pGUILoadedEffect->Delete(iStart);
    return;
  }

  WorksheetModifiedEvent Event;
  PostEvent(&Event);

  UpdateWorksheet();
}

void WorksheetWindow::InsertCommand(int iEffect, int iSubstitute)
{  
  GUILoadedEffect* pGuiEffect;
  m_pslEntryList->Get(iEffect, pGuiEffect);  

  char pcArguments[1024] = "";

  pcArguments[0] = '\0';

  GUICommand* pSubstitute;    
  if (iSubstitute != -1)
  {
    pGuiEffect->Get(iSubstitute, pSubstitute);

    m_iCurrentTick = pSubstitute->m_iTick;
      
    strcpy(pcArguments, pSubstitute->m_pCommand->GetArgs());
  }
  if (pGuiEffect->DemoEffect()->NewCommand(pcArguments, 1024) == 0)
  {
    if (iSubstitute != -1)
    {
      pGuiEffect->Delete(iSubstitute);
      delete pSubstitute;
    }

    if (InsertCommand(pGuiEffect, TCommand::CM_FX_COMMAND, pcArguments, m_iCurrentTick) == -1)
    {
      return;
    }    
    WorksheetModifiedEvent Event;
    PostEvent(&Event);

    UpdateWorksheet();
  }
}

bool WorksheetWindow::IsRangeLimit(int iEffect, int iCommand)
{
  GUILoadedEffect* pGuiEffect;
  m_pslEntryList->Get(iEffect, pGuiEffect);

  GUICommand* pCommand;
  pGuiEffect->Get(iCommand, pCommand);

  if (pCommand->m_pCommand->GetCommand() == TCommand::CM_FX_START ||
      pCommand->m_pCommand->GetCommand() == TCommand::CM_FX_STOP)
  {
    return true;
  }

  return false;
}

void WorksheetWindow::DeleteItem(int iEffect, int iCommand)
{
  if (MessageBox(Hwnd(), "Are you sure you want to delete item?", "Warning", MB_YESNO | MB_ICONWARNING) == IDYES)
  {
    GUILoadedEffect* pGuiEffect;
    m_pslEntryList->Get(iEffect, pGuiEffect);

    GUICommand* pCommand;
    pGuiEffect->Get(iCommand, pCommand);

    int iOther = -1;

    if (pCommand->m_pCommand->GetCommand() == TCommand::CM_FX_START)
    {
      iOther = pGuiEffect->GetEndCommand(iCommand);
    }
    else
    if (pCommand->m_pCommand->GetCommand() == TCommand::CM_FX_STOP)
    {
      iOther = pGuiEffect->GetStartCommand(iCommand);
    }

    pGuiEffect->Delete(iCommand, true);
    if (iOther != -1) pGuiEffect->Delete(iOther, true);

    delete pCommand;
  
    WorksheetModifiedEvent Event;
    PostEvent(&Event);
    UpdateWorksheet();
  }
}

void WorksheetWindow::CopyCommand(int iEffect, int iCommand)
{ 
  GUILoadedEffect* pGuiEffect;
  m_pslEntryList->Get(iEffect, pGuiEffect);

  GUICommand* pCommand;
  pGuiEffect->Get(iCommand, pCommand);

  m_CopyPasteBuffer.m_bSomething = true;
  m_CopyPasteBuffer.iEffect = iEffect;
  strcpy(m_CopyPasteBuffer.pcCommand, pCommand->m_pCommand->GetArgs());
}

void WorksheetWindow::PasteCommand()
{
  assert(m_CopyPasteBuffer.m_bSomething);

  GUILoadedEffect* pGuiEffect;
  m_pslEntryList->Get(m_CopyPasteBuffer.iEffect, pGuiEffect);

  InsertCommand(pGuiEffect, TCommand::CM_FX_COMMAND, m_CopyPasteBuffer.pcCommand, m_iCurrentTick);
  UpdateWorksheet();
}


void WorksheetWindow::ReloadEffect(int iEffect)
{
  GUILoadedEffect* pGUILoadedEffect;
  m_pslEntryList->Get(iEffect, pGUILoadedEffect);

  char pcArguments[1024];
  strcpy(pcArguments, pGUILoadedEffect->LoadArguments());

  InsertEffectDlgProcData Data;

  Data.m_pWorksheetWindow = this;
  Data.m_pEffect          = pGUILoadedEffect->DemoEffect();
  
  if (Data.m_pEffect->NewEffect(pcArguments, 1024) == 0)
  {
    pGUILoadedEffect->SetArguments(pcArguments);

    if (pGUILoadedEffect->DemoEffect()->Reload(pGUILoadedEffect->LoadArguments()) != 0)
    {
      pGUILoadedEffect->SetActive(false);
    }
    else
    {
      pGUILoadedEffect->SetActive(true);      
    }    

    WorksheetModifiedEvent Event;
    PostEvent(&Event);


    UpdateWorksheet();
  }  
}

void WorksheetWindow::LayerEffect(int iEffect, int iLayer)
{
  assert(iEffect >= SM_DemoScript::E_MIN_LAYER && iEffect <= SM_DemoScript::E_MAX_LAYER);
  GUILoadedEffect* pGUILoadedEffect;
  m_pslEntryList->Get(iEffect, pGUILoadedEffect);

  pGUILoadedEffect->DemoEffect()->m_iLayer = iLayer;

  UpdateWorksheet();
}

void WorksheetWindow::DeleteEffect(int iEffect)
{
  if (MessageBox(Hwnd(), "Are you sure you want to delete effect?", "Warning", MB_YESNO | MB_ICONWARNING) == IDYES)
  {
    GUILoadedEffect* pGUILoadedEffect;
    m_pslEntryList->Get(iEffect, pGUILoadedEffect);

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

    m_pslEntryList->Delete(iEffect);

    WorksheetModifiedEvent Event;
    PostEvent(&Event);

    UpdateWorksheet();
  }
}



BOOL CALLBACK WorksheetWindow::InsertEffectDlgProc( HWND _hdlg, UINT _uMsg, WPARAM _wparam, LPARAM _lparam)
{
  static InsertEffectDlgProcData* pData = 0;
  switch (_uMsg)
  {
    case WM_INITDIALOG :
    {
      pData = (InsertEffectDlgProcData*) _lparam;
      WorksheetWindow* pWS = pData->m_pWorksheetWindow;
  
      assert(pData);  
      //SetWindowPos(_hdlg,HWND_TOPMOST,0,0,1,1,SWP_NOMOVE|SWP_NOSIZE);

      // Iterate through effects
      HWND hcombo = GetDlgItem( _hdlg,IDC_INSERTEFFECTCOMBO);
      ComboBox_ResetContent( hcombo );

      int  iFound = 0;
      for (SM_DemoEffect* pEffect=EffectInstances ; pEffect ; pEffect = pEffect->m_pNextEffect)
      {
        if (!FindGUIEffect(pWS->m_pslEntryList, pEffect))
        {
          ComboBox_InsertString( hcombo, iFound, pEffect->m_pcInstanceName);
          iFound++;
        }
      }                 
      
      if (!iFound)
      {
        SM_Main::OutputError("No available effects to add");
        return FALSE;
      }
      else
      {
        ComboBox_SetCurSel   (hcombo, 0);
      }

      RECT r;
      GetClientRect(_hdlg, &r);

      SetWindowPos(_hdlg,0,
          (GetSystemMetrics(SM_CXSCREEN)-r.right)/2,
          (GetSystemMetrics(SM_CYSCREEN)-r.bottom)/2,r.right,r.bottom, SWP_NOSIZE | SWP_NOOWNERZORDER);

      return TRUE;
    }        
    case WM_COMMAND :
      switch ( LOWORD(_wparam) )
      {
      case IDOK:
        {
          HWND hcombo = GetDlgItem( _hdlg,IDC_INSERTEFFECTCOMBO);
          int  iIndex = ComboBox_GetCurSel(hcombo);
          int  iFound = 0;
          
          pData->m_pEffect = 0;
          for (SM_DemoEffect* pEffect=EffectInstances ; pEffect ; pEffect = pEffect->m_pNextEffect)
          {
            if (!FindGUIEffect(pData->m_pWorksheetWindow->m_pslEntryList, pEffect))
            {
              ComboBox_InsertString( hcombo, iFound, pEffect->m_pcInstanceName);
              if (iFound == iIndex)
              {
                pData->m_pEffect = pEffect;
                break;
              }
              iFound++;              
            }
          }                 
          assert(pData->m_pEffect);

          EndDialog( _hdlg, IDOK );
          break;
        }
      case IDCANCEL :
        EndDialog( _hdlg, IDCANCEL );
        break;
      }
      break;
  }

  return FALSE;
}

void WorksheetWindow::InsertEffect()
{
  InsertEffectDlgProcData Data;

  Data.m_pWorksheetWindow = this;
  Data.m_pEffect          = 0;
  if (DialogBoxParam(	
          SM_Main::HInstance(),
          MAKEINTRESOURCE(IDD_INSERTEFFECT),
          Hwnd(), 
          (DLGPROC)InsertEffectDlgProc,
          (LPARAM) &Data) == IDOK)
  {
      assert(Data.m_pEffect);

      char pcArguments[1024] ="";
      if (Data.m_pEffect->NewEffect(pcArguments, 1024) == 0)
      {
        WorksheetModifiedEvent Event;
        PostEvent(&Event);

        GUILoadedEffect* pGUILoadedEffect = 0;
        
        if (Data.m_pEffect->Init(pcArguments) != 0)
        {          
          goto FAILED;
        }

        pGUILoadedEffect = new GUILoadedEffect;
        if (!pGUILoadedEffect)
        {
          goto FAILED;
        }        
        
        if (pGUILoadedEffect->Init(Data.m_pEffect, pcArguments) != 0)
        {
          goto FAILED;
        }


        if (m_pslEntryList->InsertTail(pGUILoadedEffect) == -1)
        {
          goto FAILED;
        }

        pGUILoadedEffect->SetActive(true);

        UpdateWorksheet();
        return;

        FAILED:
        SM_Main::OutputError("Failed to preload %s!!!", Data.m_pEffect->m_pcInstanceName);
        Data.m_pEffect->Shutdown();
        if (pGUILoadedEffect)
        {
          delete pGUILoadedEffect;          
        }
               
        return;
      }
  }  
}

int WorksheetWindow::ProcessMessage  (const UINT message, const WPARAM wParam, const LPARAM lParam)
{    
  switch (message)
  {
  case WM_ERASEBKGND:
    return 0;
  case WM_PAINT:
    Paint();
    return 0;

  case WM_SIZE:
    UpdateScrollBar();
    InvalidateRect(m_hwnd, 0, FALSE);
    break;

  case WM_MOUSEMOVE:
  {    
    if (m_bDrag && !(wParam & MK_LBUTTON))
    {        
      m_bDrag = false;            
    }

    if (m_bDragEffect && !(wParam & MK_LBUTTON))
    {        
      UpdateWorksheet();
      m_bDragEffect = false;                  
    }

    int x = LOWORD(lParam);
    int y = HIWORD(lParam);

    /*
    int iEffect;
    if ((iEffect = GetWidgetFromCoord(x, y, sWidget::E_EFFECT)) == -1)
    {
      m_iCurrentEffect = -1;      
    }
    else
    {
      sWidget* pWidget;
      m_slWidgets.Get(iEffect, pWidget);

      m_iCurrentEffect = pWidget->iGUIEffect;
      m_iCurrentTick   = PixelToTick(x);
    }
    */

    if (m_bDrag)
    {
      int iWidget;
      sWidget sAux;

      // Linear search, as the widgets ids can change with every redraw. Probably can get 
      // rethinked in a better way
      for (iWidget = m_slWidgets.First() ; iWidget != -1 ; iWidget = m_slWidgets.Next(iWidget))
      {
        m_slWidgets.Get(iWidget, sAux);
        if (sAux == m_CurrentWidget)
        {
          break;
        }
      }

      if (iWidget != -1)
      {
        if (sAux.eType == sWidget::E_COMMAND)
        {
          int iTick = PixelToTick(x);
          iTick = max(0, iTick);
      
          UpdateWidgetTick(iWidget, iTick);
          m_slWidgets.Get(iWidget, m_CurrentWidget);

          WorksheetModifiedEvent Event;
          PostEvent(&Event);

          UpdateWorksheet();
        }
        else if (sAux.eType == sWidget::E_RANGE)
        {
          int iTick = PixelToTick(x);

          SM_Main::OutputDebug("iTick: %i iCurrent: %i\n", iTick, m_iCurrentTick);
          
          GUILoadedEffect*  pEffect;        
          m_pslEntryList->Get(m_CurrentWidget.iGUIEffect, pEffect);
        

          int iStartCommand = m_CurrentWidget.iCommand;
          int iEndCommand   = pEffect->GetEndCommand(iStartCommand);
          int iMove         = iTick - m_iCurrentTick;

          int iMoveCommand = iMove<0?iStartCommand:iEndCommand;

          GUICommand* pCommand;    
          pEffect->Get(iMoveCommand, pCommand);
                      
          if (pEffect->CanMoveWithoutIntersection (iMoveCommand, pCommand->m_iTick+iMove))
          {
            // Update tick for next drag
            m_iCurrentTick = iTick;

            // Update range
            
            GUICommand* pStartCommand;
            GUICommand* pEndCommand;
            pEffect->Get(iStartCommand, pStartCommand);
            pEffect->Get(iEndCommand, pEndCommand);                       
            pEffect->Delete(iStartCommand);
            pEffect->Delete(iEndCommand);

            int iRangeLength = pEndCommand->m_iTick - pStartCommand->m_iTick;
            pStartCommand->m_iTick = max(0, pStartCommand->m_iTick+iMove);
            pEndCommand->m_iTick = pStartCommand->m_iTick + iRangeLength;
            
            m_CurrentWidget.iCommand = pEffect->Insert(pStartCommand);
            pEffect->Insert(pEndCommand);
  
            WorksheetModifiedEvent Event;
            PostEvent(&Event);

            
            UpdateWorksheet();
          }          
          else
          {
            SM_Main::OutputConsole("Cant move\n");
          }
        }
      }
    }
    else
    {
      int iWidget = GetWidgetFromCoord(x, y, sWidget::E_COMMAND);

      char pcText[1024] = "";
      if (iWidget != -1)
      {
        // Update command text
        sWidget*          pWidget;
        GUILoadedEffect*  pEffect;
        GUICommand*       pCommand;

        m_slWidgets.Get(iWidget, pWidget);
        m_pslEntryList->Get(pWidget->iGUIEffect, pEffect);
        pEffect->Get(pWidget->iCommand, pCommand);
        
        pCommand->m_pCommand->ToString(pcText, 1024);

        m_slWidgets.Get(iWidget, m_CurrentWidget);
      }      
      else if ((iWidget = GetWidgetFromCoord(x, y, sWidget::E_EFFECT)) != -1)
      {
        // Update effect text
        sWidget*          pWidget;        
        GUILoadedEffect*  pEffect;
        
        m_slWidgets.Get(iWidget, pWidget);        
        m_pslEntryList->Get(pWidget->iGUIEffect, pEffect);

        strncpy(pcText, pEffect->LoadArguments(), 1024);
        pcText[1023] = '\0';                    
        
        m_CurrentWidget.iCommand = -1;
      }
      
      WorksheetMarkEvent Event(pcText);
      PostEvent(&Event);
    }

    if (m_bDragEffect)
    {
      int iWidget;
      if ( (iWidget = GetWidgetFromCoord(x, y, sWidget::E_EFFECT)) != -1)
      {
        sWidget*          pWidget;        
        
        m_slWidgets.Get(iWidget, pWidget);        

        GUILoadedEffect*  pEffect;        

        if (m_iCurrentEffect != pWidget->iGUIEffect)
        {
          bool bBefore = true;

          int iIterator;
          for (iIterator = m_pslEntryList->First() ; iIterator != -1 ; iIterator = m_pslEntryList->Next(iIterator))
          {
            if (iIterator == pWidget->iGUIEffect)
            {
              break;
            }

            if (iIterator == m_iCurrentEffect)
            {
              bBefore = false;
              break;
            }
          }

          m_pslEntryList->Get(m_iCurrentEffect, pEffect);
          m_pslEntryList->Delete(m_iCurrentEffect);               

          if (bBefore)
          {
            m_iCurrentEffect = m_pslEntryList->InsertBefore(pWidget->iGUIEffect, pEffect);
          }
          else
          {
            m_iCurrentEffect = m_pslEntryList->InsertAfter(pWidget->iGUIEffect, pEffect);
          }
          
        
          // Insert before
          SM_Main::OutputDebug("Dragging effect...\n");
          UpdateWorksheet         ();
        }
      }
    }

    break;
  }

  case WM_LBUTTONDBLCLK:
  {
    SM_Main::OutputDebug("Dbl click\n");
    int iWidget;

    int x = GET_X_LPARAM(lParam);
    int y = GET_Y_LPARAM(lParam);

    if ((iWidget = GetWidgetFromCoord(x, y, sWidget::E_COMMAND)) != -1)
    {
      GUILoadedEffect*  pEffect;
      GUICommand*       pCommand;
      sWidget*          pWidget;

      m_slWidgets.Get(iWidget, pWidget);
      m_pslEntryList->Get(pWidget->iGUIEffect, pEffect);
      pEffect->Get(pWidget->iCommand, pCommand);  
     
      if (pCommand->m_pCommand->GetCommand() == TCommand::CM_FX_START ||
          pCommand->m_pCommand->GetCommand() == TCommand::CM_FX_STOP)
      {
        break;
      }

      InsertCommand(pWidget->iGUIEffect, pWidget->iCommand);
    }
    else
    if ((iWidget = GetWidgetFromCoord(x, y, sWidget::E_EFFECT)) != -1)
    {           
      SM_Main::OutputDebug("Dbl click for effect reloading\n");
    }

    break;
  }

  case WM_LBUTTONDOWN:
  {
    int x = LOWORD(lParam);
    int y = HIWORD(lParam);

    int iWidget;
    if ( !m_bDrag )
    {
      if ((iWidget = GetWidgetFromCoord(x, y, sWidget::E_COMMAND)) != -1)
      {
        SM_Main::OutputConsole("Widget %i pointed\n", iWidget);
        m_iCurrentTick   = PixelToTick(x);
        m_iCurrentX      = x;
        m_bDrag = true;
      }
      else if ((iWidget = GetWidgetFromCoord(x, y, sWidget::E_RANGE)) != -1)
      {
        SM_Main::OutputConsole("Widget %i pointed\n", iWidget);
        m_iCurrentTick   = PixelToTick(x);
        m_iCurrentX      = x;
        m_bDrag = true;
      }      
    }  
       
    if (iWidget != -1)
    {
      m_slWidgets.Get(iWidget, m_CurrentWidget);    

      GUILoadedEffect*  pEffect;        
      m_pslEntryList->Get(m_CurrentWidget.iGUIEffect, pEffect);
      if (!pEffect->GetActive())
      {
        m_bDrag = false;
        break;
      }
    }

    if ( !m_bDragEffect )
    {
      int iEffect;
      if ((iEffect = GetWidgetFromCoord(x, y, sWidget::E_EFFECT)) != -1)
      {
        if (x < TITLEWIDTH)
        {
          SM_Main::OutputDebug("Drag effect\n");
          sWidget* pWidget;
          m_slWidgets.Get(iEffect, pWidget);
          m_iCurrentEffect = pWidget->iGUIEffect;

          m_bDragEffect = true;
          UpdateWorksheet();
        }
      }
    }

    break;
  }
  case WM_RBUTTONDOWN:
  {
    if (!m_bActive) break;

    int x = LOWORD(lParam);
    int y = HIWORD(lParam);

    int iEffect;
    if ((iEffect = GetWidgetFromCoord(x, y, sWidget::E_EFFECT)) == -1)
    {
      m_iCurrentEffect = -1;      
    }
    else
    {
      sWidget* pWidget;
      m_slWidgets.Get(iEffect, pWidget);

      m_iCurrentEffect = pWidget->iGUIEffect;
      m_iCurrentTick   = PixelToTick(x);
    }

    HMENU hmenu = 0;
    bool bEffect; // Flags if we're working on effects or commands

    int iWidget = -1;
    if (x < TITLEWIDTH)
    {
      bEffect = true;
    }
    else
    {
      if ((iWidget = GetWidgetFromCoord(x, y, sWidget::E_COMMAND)) != -1)
      {
        SM_Main::OutputConsole("Widget %i pointed\n", iWidget);
        m_iCurrentTick   = PixelToTick(x);
        m_iCurrentX      = x;        
      }
      else if ((iWidget = GetWidgetFromCoord(x, y, sWidget::E_RANGE)) != -1)
      {
        SM_Main::OutputConsole("Widget %i pointed\n", iWidget);
        m_iCurrentTick   = PixelToTick(x);
        m_iCurrentX      = x;        
      }

      // Fetch widget
      if (iWidget != -1)
      {
        m_slWidgets.Get(iWidget, m_CurrentWidget); 
      }
      
      bEffect = false;
    }

    // If effect isn't active, bail
    if (m_iCurrentEffect != -1 && !bEffect)
    {
      GUILoadedEffect*  pEffect;        
      m_pslEntryList->Get(m_iCurrentEffect, pEffect);
      if (!pEffect->GetActive())
      {
        break;
      }
    }

    // Load appropiate menu
    if (bEffect)
    {
      hmenu = LoadMenu(SM_Main::HInstance(), MAKEINTRESOURCE(IDR_WORKSHEETLIST));
      
    }
    else
    {
      hmenu = LoadMenu(SM_Main::HInstance(), MAKEINTRESOURCE(IDR_WORKSHEETCONTEXT));
    }

    // Did we fail?     
    if (hmenu == 0)
    {
      SM_Main::OutputError("Failed to load context menu\n");
      break;
    }

    HMENU htrack = GetSubMenu(hmenu, 0);

    POINT p = { x, y};
    
    ClientToScreen(m_hwnd, &p);

    x = p.x; 
    y = p.y;

    // Select menu options
    if (bEffect == false)
    {
      bool bEnable = true;

      if (iWidget == -1)
      {
        bEnable = false;
      }
      
      EnableMenuItem(htrack, ID_WORKSHEETCOMMAND_DELETE, MF_BYCOMMAND | (bEnable?MF_ENABLED:MF_GRAYED));      
      EnableMenuItem(htrack, ID_WORKSHEETCOMMAND_COPY, MF_BYCOMMAND | ((m_iCurrentEffect == -1 || m_CurrentWidget.iCommand == -1 || IsRangeLimit(m_iCurrentEffect, m_CurrentWidget.iCommand))?MF_GRAYED:MF_ENABLED));      
      EnableMenuItem(htrack, ID_WORKSHEETCOMMAND_PASTE, MF_BYCOMMAND | ((m_CopyPasteBuffer.m_bSomething && m_iCurrentEffect == m_CopyPasteBuffer.iEffect)?MF_ENABLED:MF_GRAYED));
    }
    else
    {      
      EnableMenuItem(htrack, ID_EFFECTCOMMAND_RELOAD, MF_BYCOMMAND | (m_iCurrentEffect!=-1?MF_ENABLED:MF_GRAYED));
      EnableMenuItem(htrack, ID_EFFECTCOMMAND_REMOVEEFFECT, MF_BYCOMMAND | (m_iCurrentEffect!=-1?MF_ENABLED:MF_GRAYED));

      // @BUG: This isnt working :|
      EnableMenuItem(htrack, ID_EFFECTCOMMAND_LAYER, MF_BYCOMMAND | (m_iCurrentEffect!=-1?MF_ENABLED:MF_GRAYED));

      // There has to be a better way to do this... (workaround of previous line)
      EnableMenuItem(htrack, ID_LAYER_0, MF_BYCOMMAND | (m_iCurrentEffect!=-1?MF_ENABLED:MF_GRAYED));
      EnableMenuItem(htrack, ID_LAYER_1, MF_BYCOMMAND | (m_iCurrentEffect!=-1?MF_ENABLED:MF_GRAYED));
      EnableMenuItem(htrack, ID_LAYER_2, MF_BYCOMMAND | (m_iCurrentEffect!=-1?MF_ENABLED:MF_GRAYED));
      EnableMenuItem(htrack, ID_LAYER_3, MF_BYCOMMAND | (m_iCurrentEffect!=-1?MF_ENABLED:MF_GRAYED));
      EnableMenuItem(htrack, ID_LAYER_4, MF_BYCOMMAND | (m_iCurrentEffect!=-1?MF_ENABLED:MF_GRAYED));
      EnableMenuItem(htrack, ID_LAYER_5, MF_BYCOMMAND | (m_iCurrentEffect!=-1?MF_ENABLED:MF_GRAYED));
      EnableMenuItem(htrack, ID_LAYER_6, MF_BYCOMMAND | (m_iCurrentEffect!=-1?MF_ENABLED:MF_GRAYED));
      EnableMenuItem(htrack, ID_LAYER_7, MF_BYCOMMAND | (m_iCurrentEffect!=-1?MF_ENABLED:MF_GRAYED));
      EnableMenuItem(htrack, ID_LAYER_8, MF_BYCOMMAND | (m_iCurrentEffect!=-1?MF_ENABLED:MF_GRAYED));
      EnableMenuItem(htrack, ID_LAYER_9, MF_BYCOMMAND | (m_iCurrentEffect!=-1?MF_ENABLED:MF_GRAYED));
      EnableMenuItem(htrack, ID_LAYER_10, MF_BYCOMMAND | (m_iCurrentEffect!=-1?MF_ENABLED:MF_GRAYED));
      EnableMenuItem(htrack, ID_LAYER_11, MF_BYCOMMAND | (m_iCurrentEffect!=-1?MF_ENABLED:MF_GRAYED));
      EnableMenuItem(htrack, ID_LAYER_12, MF_BYCOMMAND | (m_iCurrentEffect!=-1?MF_ENABLED:MF_GRAYED));
      EnableMenuItem(htrack, ID_LAYER_13, MF_BYCOMMAND | (m_iCurrentEffect!=-1?MF_ENABLED:MF_GRAYED));
      EnableMenuItem(htrack, ID_LAYER_14, MF_BYCOMMAND | (m_iCurrentEffect!=-1?MF_ENABLED:MF_GRAYED));
      EnableMenuItem(htrack, ID_LAYER_15, MF_BYCOMMAND | (m_iCurrentEffect!=-1?MF_ENABLED:MF_GRAYED));
    }

    TrackPopupMenu(htrack, 
                   TPM_CENTERALIGN | TPM_VCENTERALIGN | TPM_RIGHTBUTTON,
                   x, y, 0, m_hwnd, NULL);

    DestroyMenu(hmenu);

    break;
  }    
  

  case WM_LBUTTONUP:
  {
    m_bDrag   = false;    
    
    if (m_bDragEffect)
    {
      m_bDragEffect = false;
      UpdateWorksheet         ();      
    }
    break;
  }

  case WM_VSCROLL: 
    { 
      int iPos = m_iEntryOffset;
      bool bUpdate = true;
      switch (LOWORD(wParam)) 
      { 
        case SB_PAGEUP: 
            iPos -= m_iPageSize; 
            break; 
        case SB_PAGEDOWN: 
            iPos += m_iPageSize; 
            break; 
        case SB_LINEUP: 
            iPos --; 
            break; 
        case SB_LINEDOWN: 
            iPos++; 
            break; 
        case SB_THUMBTRACK:                  
        case SB_THUMBPOSITION: 
            iPos = HIWORD(wParam);              
            break; 
        default:
          bUpdate = false;
          break;
      } 

      if (bUpdate)
      {
        iPos = min(m_pslEntryList->GetNumberElements()-m_iPageSize, max(0, iPos));
        SCROLLINFO si;
        si.cbSize = sizeof(SCROLLINFO);
        si.fMask  = SIF_POS; 
        si.nPos   = iPos;

        SetScrollInfo(m_hwnd, SB_VERT, &si, TRUE); 

        m_iEntryOffset = iPos;

        InvalidateRect(m_hwnd, 0, FALSE);
      }                      
    } 

  case WM_COMMAND:
    {
      int iMenu = wParam & 0xFFFF;
      switch (wParam)
      {
      case ID_EFFECTCOMMAND_INSERTEFFECT:
        InsertEffect();
        break;
      case ID_EFFECTCOMMAND_REMOVEEFFECT:        
        if (m_iCurrentEffect != -1)
        {
          DeleteEffect(m_iCurrentEffect);
        }
        break;
      case ID_EFFECTCOMMAND_RELOAD:
        if (m_iCurrentEffect != -1)
        {
          ReloadEffect(m_iCurrentEffect);
        }
        break;
      case ID_WORKSHEETCOMMAND_COPY:
        CopyCommand(m_iCurrentEffect, m_CurrentWidget.iCommand);
        break;
      case ID_WORKSHEETCOMMAND_PASTE:
        PasteCommand();
        break;      
        
      case ID_WORKSHEETCOMMAND_NEWRANGE:
        if (m_iCurrentEffect != -1)
        {
          InsertRange(m_iCurrentEffect);
        }
        break;
      case ID_WORKSHEETCOMMAND_NEWCOMMAND:
        if (m_iCurrentEffect != -1)
        {
          InsertCommand(m_iCurrentEffect);
        }
        break;
      case ID_WORKSHEETCOMMAND_DELETE:
        {
          DeleteItem(m_iCurrentEffect, m_CurrentWidget.iCommand);
        }
        break;      
      case ID_LAYER_0 : LayerEffect(m_iCurrentEffect, 0 ); break;
      case ID_LAYER_1 : LayerEffect(m_iCurrentEffect, 1 ); break;
      case ID_LAYER_2 : LayerEffect(m_iCurrentEffect, 2 ); break;
      case ID_LAYER_3 : LayerEffect(m_iCurrentEffect, 3 ); break;
      case ID_LAYER_4 : LayerEffect(m_iCurrentEffect, 4 ); break;
      case ID_LAYER_5 : LayerEffect(m_iCurrentEffect, 5 ); break;
      case ID_LAYER_6 : LayerEffect(m_iCurrentEffect, 6 ); break;
      case ID_LAYER_7 : LayerEffect(m_iCurrentEffect, 7 ); break;
      case ID_LAYER_8 : LayerEffect(m_iCurrentEffect, 8 ); break;
      case ID_LAYER_9 : LayerEffect(m_iCurrentEffect, 9 ); break;
      case ID_LAYER_10: LayerEffect(m_iCurrentEffect, 10); break;
      case ID_LAYER_11: LayerEffect(m_iCurrentEffect, 11); break;
      case ID_LAYER_12: LayerEffect(m_iCurrentEffect, 12); break;
      case ID_LAYER_13: LayerEffect(m_iCurrentEffect, 13); break;
      case ID_LAYER_14: LayerEffect(m_iCurrentEffect, 14); break;
      case ID_LAYER_15: LayerEffect(m_iCurrentEffect, 15); break;        
      }
      break;      
    }
  }
  return Window::ProcessMessage(message, wParam, lParam);
}

void WorksheetWindow::PaintLimit(HDC hdc, int x, int y, HRGN* pLimit)
{
  POINT arr[5];

  #define WIDTH 8  

  arr[0].x = x            ; arr[0].y = y+BARHEIGHT;
  arr[1].x = x            ; arr[1].y = y-1;
  arr[2].x = x+WIDTH/2    ; arr[2].y = y-WIDTH/4-1;
  arr[3].x = x+WIDTH      ; arr[3].y = y-1;
  arr[4].x = x+WIDTH      ; arr[4].y = y+BARHEIGHT;
  

  int iPoints = 5;

  HGDIOBJ hPrevBrush = SelectObject(hdc, hLimitFill); 
  PolyPolygon(hdc, arr, &iPoints,  1);
  SelectObject(hdc, hPrevBrush); 

  HGDIOBJ hPrevPen = SelectObject(hdc, hLimitOutline); 
  Polyline(hdc, arr, 3);
  SelectObject(hdc, hPrevPen);     

  if (pLimit)
  {
    *pLimit = CreatePolygonRgn(arr, iPoints, ALTERNATE);
  }
}

void WorksheetWindow::PaintCommand(HDC hdc, int x, int y, HRGN* pCommand)
{
  POINT arr[4];

  #define COMMANDWIDTH 10

  y+=10;

  
  arr[0].x = x                ; arr[0].y = y+COMMANDWIDTH/2;
  arr[1].x = x-COMMANDWIDTH/2 ; arr[1].y = y;  
  arr[2].x = x                ; arr[2].y = y-COMMANDWIDTH/4-3;
  arr[3].x = x+COMMANDWIDTH/2 ; arr[3].y = y;
  
  int iPoints = 4;

  HGDIOBJ hPrevBrush = SelectObject(hdc, hBrushYellow); 
  PolyPolygon(hdc, arr, &iPoints,  1);
  SelectObject(hdc, hPrevBrush); 

  HGDIOBJ hPrevPen = SelectObject(hdc, hPenHalfYellow); 
  Polyline(hdc, arr, 3);
  SelectObject(hdc, hPrevPen);    

  if (pCommand)
  {
    *pCommand = CreatePolygonRgn(arr, iPoints, ALTERNATE);
  }
}

void WorksheetWindow::PaintBar(HDC hdc, int y, int iStart, int iEnd, 
                               HRGN* pBeginRegion,
                               HRGN* pEndRegion,
                               HRGN* pBarRegion)
{
  unsigned uStartX = iStart;
  unsigned uStartY = y+6;
  unsigned uLength = iEnd - iStart;

  
  RECT rect;
  rect.left   = uStartX; rect.top    = uStartY ; rect.bottom = uStartY+1; rect.right  = uStartX+uLength;
  FillRect(hdc, &rect, hBrushBlack);       

  rect.left   = uStartX; rect.top    = uStartY+1 ; rect.bottom = uStartY+2; rect.right  = uStartX+uLength;
  FillRect(hdc, &rect, hBrushGrey);       

  rect.left   = uStartX; rect.top    = uStartY+2 ; rect.bottom = uStartY+BARHEIGHT-1; rect.right  = uStartX+uLength;
  FillRect(hdc, &rect, hBrushRed);       

  rect.left   = uStartX; rect.top    = uStartY+BARHEIGHT-1 ; rect.bottom = uStartY+BARHEIGHT; rect.right  = uStartX+uLength;
  FillRect(hdc, &rect, hBrushHalfRed);       

  if (pBarRegion)
  {
    *pBarRegion = CreateRectRgn(uStartX, uStartY, uStartX+uLength, uStartY+BARHEIGHT);
  }
   

  PaintLimit(hdc, uStartX, uStartY, pBeginRegion);
  PaintLimit(hdc, uStartX + uLength, uStartY, pEndRegion);  
}

void WorksheetWindow::PaintEntry(HDC hdc, int iEntry, int y)
{
  int iIterator;

  bool bInRange     = false;
  int  iStartTick;
  int  iStopTick;
  int  iStartCommand;
  int  iEndCommand;

  int w,h;
  GetClientSize(&w, &h);

  GUILoadedEffect* pEntry = 0;
  m_pslEntryList->Get(iEntry, pEntry);
  assert(pEntry);

  int iEndTick = m_DemoIDEView.m_iStartTick + m_DemoIDEView.m_iScale * ((w - TITLEWIDTH) / TICKWIDTH);
      

  HRGN hrgn = CreateRectRgn(TITLEWIDTH, 0, w, h);
  SelectClipRgn(hdc, hrgn);

  for (iIterator = pEntry->First() ;
       iIterator != -1 ;
       iIterator = pEntry->Next(iIterator))
  {
    GUICommand* pCommand;

    pEntry->Get(iIterator, pCommand);

    switch (pCommand->m_pCommand->GetCommand())
    {
    case TCommand::CM_FX_START:
      assert(bInRange == false);
      bInRange = true;

      iStartTick    = pCommand->m_iTick;
      iStartCommand = iIterator;
      break;
    case TCommand::CM_FX_STOP:
      assert(bInRange == true);
      bInRange = false;

      iStopTick   = pCommand->m_iTick;
      iEndCommand = iIterator;
      
      // Paint range
      if ( !((iStopTick < iStartTick) ||  (m_DemoIDEView.m_iStartTick > iEndTick)) )
      {
        HRGN BeginRegion, EndRegion, BarRegion;
        PaintBar(hdc, 
                 y, 
                 TickToPixel(iStartTick), 
                 TickToPixel(iStopTick),
                 &BeginRegion,
                 &EndRegion,
                 &BarRegion);

        // Add widgets to list
        sWidget Widget;

        Widget.eType      = sWidget::E_COMMAND;
        Widget.iCommand   = iStartCommand;
        Widget.iGUIEffect = iEntry;
        Widget.hRegion    = BeginRegion;

        assert(Widget.hRegion);

        if (m_slWidgets.InsertTail(Widget) == -1)
        {
          DeleteObject(Widget.hRegion);
        }

        Widget.eType      = sWidget::E_COMMAND;
        Widget.iCommand   = iEndCommand;
        Widget.iGUIEffect = iEntry;
        Widget.hRegion    = EndRegion;

        assert(Widget.hRegion);

        if (m_slWidgets.InsertTail(Widget) == -1)
        {
          DeleteObject(Widget.hRegion);
        }

        Widget.eType      = sWidget::E_RANGE;
        Widget.iCommand   = iStartCommand; // We can find iEndCommand from iStartCommand easily
        Widget.iGUIEffect = iEntry;
        Widget.hRegion    = BarRegion;

        assert(Widget.hRegion);

        if (m_slWidgets.InsertTail(Widget) == -1)
        {
          DeleteObject(Widget.hRegion);
        }
      }
      break;
    }
  }

  for (iIterator = pEntry->First() ;
      iIterator != -1 ;
      iIterator = pEntry->Next(iIterator))
  {
    GUICommand* pCommand;

    pEntry->Get(iIterator, pCommand);

    switch (pCommand->m_pCommand->GetCommand())
    {
    case TCommand::CM_FX_COMMAND:
        sWidget Widget;
        HRGN CommandRegion;
        PaintCommand(hdc, TickToPixel(pCommand->m_iTick), y, &CommandRegion);

        Widget.eType      = sWidget::E_COMMAND;
        Widget.iCommand   = iIterator;
        Widget.iGUIEffect = iEntry;
        Widget.hRegion    = CommandRegion;

        assert(Widget.hRegion);

        if (m_slWidgets.InsertTail(Widget) == -1)
        {
          DeleteObject(Widget.hRegion);
        }

        break;
    }
  }
    
  SelectClipRgn(hdc, 0);
  DeleteObject(hrgn);
  
  
  // Insert widget
  hrgn = CreateRectRgn(0, y, w, y+ROWHEIGHT);

  sWidget Widget;
  Widget.eType      = sWidget::E_EFFECT;
  Widget.hRegion    = hrgn;
  Widget.iCommand   = -1;
  Widget.iGUIEffect = iEntry;

  
  if (m_slWidgets.InsertTail(Widget) == -1)
  {
    DeleteObject(Widget.hRegion);    
  }
}

void WorksheetWindow::ShutdownWidgetList()
{
  int iIterator;

  for (iIterator = m_slWidgets.First() ; iIterator != -1 ; iIterator = m_slWidgets.Next(iIterator))
  {
    sWidget* pWidget;
    m_slWidgets.Get(iIterator, pWidget);

    assert(pWidget->hRegion);
    DeleteObject(pWidget->hRegion);
  }

  m_slWidgets.Shutdown();
}

void WorksheetWindow::ResetWidgetList()
{
  ShutdownWidgetList();
  m_slWidgets.Init();
}

void WorksheetWindow::PaintBPM(HDC hdc, int h)
{
  if (m_DemoIDEView.m_bBPM)
  {
    float fBeatsPerSecond = float(m_DemoIDEView.m_iBPM) / 60.0f;
    float fTicksPerBeat    = float(TICKSPERSECOND) / fBeatsPerSecond;

    int w,h;

    GetClientSize(&w, &h);
   
    float fStartBeat = float(int(max(0.0, float(m_DemoIDEView.m_iStartBPM - m_DemoIDEView.m_iStartTick)/fTicksPerBeat)));
    
    float fStartTick = fStartBeat*fTicksPerBeat;

    float fMaximumTick = (float) PixelToTick(w);

   
    for (float fCurrentTick = (float) m_DemoIDEView.m_iStartBPM ; fCurrentTick < fMaximumTick ; fCurrentTick += fTicksPerBeat)
    {

      PaintCue(hdc, int(fCurrentTick), h, hBrushBPM);
      // TICKWIDTH
    } 
    /*
    for (float fCurrentTick = fStartTick ; fCurrentTick < fMaximumTick ; fCurrentTick += fTicksPerBeat)
    {

      PaintCue(hdc, int(fCurrentTick), h);
      // TICKWIDTH
    } 
    */
  }
}


void WorksheetWindow::PaintCue(HDC hdc, int iTick, int h, HBRUSH hBrush)
{
  RECT rect;
  rect.left   = TickToPixel(iTick); 
  rect.top    = 0 ; 
  rect.bottom = h; 
  rect.right  = rect.left+1;

  if (rect.left >= TITLEWIDTH)
  {
    FillRect(hdc, &rect, hBrush);           
  }
}

void WorksheetWindow::PaintPlaying(HDC hdc, int iTick, int h)
{
  RECT rect;
  rect.left   = TickToPixel(iTick); 
  rect.top    = 0 ; 
  rect.bottom = h; 
  rect.right  = rect.left+1;

  if (rect.left >= TITLEWIDTH)
  {
    FillRect(hdc, &rect, hBrushRed);           
  }  
}

void WorksheetWindow::DoPaintPlaying()
{
  HDC         hdcwindow;
  PAINTSTRUCT ps;
  int         w,h;

  GetClientSize(&w, &h);

  hdcwindow = BeginPaint(m_hwnd, &ps);

  HDC hdc= CreateCompatibleDC(hdcwindow);
  HBITMAP hbitmap = CreateCompatibleBitmap(hdcwindow, w, h);
  HBITMAP holdbitmap = (HBITMAP) SelectObject(hdc, hbitmap);

  PaintPlaying(hdc, m_DemoIDEView.m_iPlaytick, h);

  BitBlt(hdcwindow, 0, 0, w, h, hdc, 0, 0, SRCCOPY);
  DeleteDC(hdc);
  DeleteObject(hbitmap);  
  
  EndPaint(m_hwnd, &ps);               
}

void WorksheetWindow::Paint()
{ 
  HDC hdcwindow;
  PAINTSTRUCT ps;

  int w,h;
  GetClientSize(&w, &h);

  // Reset widget list
  ResetWidgetList();

  hdcwindow = BeginPaint(m_hwnd, &ps);

  HDC hdc= CreateCompatibleDC(hdcwindow);
  HBITMAP hbitmap = CreateCompatibleBitmap(hdcwindow, w, h);
  HBITMAP holdbitmap = (HBITMAP) SelectObject(hdc, hbitmap);
    
  RECT rect;

  int iIterator;
  int iOffset = 0;
  int iCount  = 0;
  int i;

  for (i = 0 , iIterator = m_pslEntryList->First() ; 
       i < m_iEntryOffset && iIterator != -1 ; 
       iIterator = m_pslEntryList->Next(iIterator), i++);

  for (iIterator  ; iIterator != -1  ; iIterator = m_pslEntryList->Next(iIterator), iCount++, iOffset+=ROWHEIGHT)
  {
    GUILoadedEffect* pEntry;
    m_pslEntryList->Get(iIterator, pEntry);
      
    rect.left   = 0; rect.top    = iOffset; rect.bottom = iOffset+ROWHEIGHT; rect.right  = w;
    FillRect(hdc, &rect, iCount%2==0?hBrushEven:hBrushOdd);    
    
    rect.left   = 0; rect.top    = iOffset ; rect.bottom = iOffset+1; rect.right  = w;
    FillRect(hdc, &rect, hBrushBlack);       

    SelectObject (hdc, hfont) ;
    SetBkMode(hdc, TRANSPARENT);

    char pcText[128];
    sprintf(pcText, "%s (Layer %i)", pEntry->DemoEffect()->m_pcInstanceName, pEntry->DemoEffect()->m_iLayer);
    TextOut (hdc, 2, iOffset+2, pcText, strlen(pcText)) ;

    // Paint loaded effect
    PaintEntry(hdc, iIterator, iOffset);

    if (!pEntry->GetActive())
    {
      RECT rect;
      rect.left   = TITLEWIDTH; rect.top    = iOffset ; rect.bottom = iOffset+ROWHEIGHT; rect.right  = w;
      PaintInactive(hdc, &rect);
    }

    if (m_bDragEffect)
    {
      if (iIterator == m_iCurrentEffect)
      {
        rect.left   = 0; rect.top    = iOffset ; rect.bottom = iOffset+2; rect.right  = w;
        FillRect(hdc, &rect, hBrushBlack);       

        rect.left   = 0; rect.top    = iOffset+ROWHEIGHT-1 ; rect.bottom = iOffset+ROWHEIGHT; rect.right  = w;
        FillRect(hdc, &rect, hBrushBlack);       
      }
    }  
  }

  for ( ; iCount < h/ROWHEIGHT+1 ; iCount++, iOffset += ROWHEIGHT)
  {
    rect.left   = 0; rect.top    = iOffset; rect.bottom = iOffset+ROWHEIGHT; rect.right  = w;
    FillRect(hdc, &rect, iCount%2==0?hBrushEven:hBrushOdd);    
    
    rect.left   = 0; rect.top    = iOffset ; rect.bottom = iOffset+1; rect.right  = w;
    FillRect(hdc, &rect, hBrushBlack);       
  }

  rect.left   = TITLEWIDTH; rect.top    = 0 ; rect.bottom = h; rect.right  = TITLEWIDTH+1;
  FillRect(hdc, &rect, hBrushBlack);       

  PaintBPM(hdc, h);
  PaintCue(hdc, m_DemoIDEView.m_iCurrentTick, h, hBrushGreen);
  PaintPlaying(hdc, m_DemoIDEView.m_iPlaytick, h);

  if (!m_bActive)
  {
    RECT rect;
    GetClientRect(Hwnd(), &rect);    
    
    PaintInactive(hdc, &rect);    
  }

  BitBlt(hdcwindow, 0, 0, w, h, hdc, 0, 0, SRCCOPY);
  DeleteDC(hdc);
  DeleteObject(hbitmap);
  
  EndPaint(m_hwnd, &ps);             
}

// Paints a region as inactive
void WorksheetWindow::PaintInactive(HDC hdc, RECT* pRect)
{
  HBRUSH hInactive = CreateHatchBrush(HS_FDIAGONAL, RGB(0, 0, 0));
   
  SetBkMode(hdc, TRANSPARENT);
  
  HBRUSH hold = (HBRUSH) SelectObject(hdc, hInactive);

  PatBlt(hdc, pRect->left, pRect->top, pRect->right, pRect->bottom, PATINVERT);
  PatBlt(hdc, pRect->left, pRect->top, pRect->right, pRect->bottom, DSTINVERT);
  
  SetBkMode(hdc, OPAQUE);

  SelectObject(hdc, hold);
  
  DeleteObject(hInactive);
}

int WorksheetWindow::UpdateWorksheet()
{
  int iReturn = -1;

  
  // Update the control
  UpdateScrollBar(); 
  
  WorksheetUpdateEvent Event;
  PostEvent(&Event);

  iReturn = 0;
  
  InvalidateRect(m_hwnd, 0, TRUE);
  UpdateWindow(m_hwnd);
  


  return iReturn;
}

void WorksheetWindow::SetView(const DemoIDEView* pDemoIDEView, bool bForceRedraw)
{
  m_DemoIDEView = *pDemoIDEView;

  if (bForceRedraw)
  {
    InvalidateRect(m_hwnd, 0, FALSE);
    UpdateWindow(m_hwnd);
  }
  else
  {
    InvalidateRect(m_hwnd, 0, FALSE);    
    UpdateWindow(m_hwnd);
    //DoPaintPlaying();
  }

}

const DemoIDEView* WorksheetWindow::GetView()
{
  return &m_DemoIDEView;
}