// Choice.cpp : Implementation of cChoice
#include "stdafx.h"
#include "DecalControls.h"
#include "Choice.h"

#include "ChoicePopup.h"
#include "ChoiceDropDown.h"

/////////////////////////////////////////////////////////////////////////////
// cChoice

cChoice::cChoice()
: m_nSelected( -1 ),
m_nHotSelect( -1 ),
m_nDropLines( 8 ),
m_bPopup( false ),
m_bMouseDown( false ),
m_bMouseOver( false )
{
}

void cChoice::setPopup( bool bPopup )
{
   // Always reset hot select
   m_bPopup = bPopup;
   m_nHotSelect = -1;

   if( bPopup )
   {
      long nID;
      m_pSite->get_ID( &nID );
      Fire_DropDown( nID );
      m_pPopup->put_Popup( VARIANT_TRUE );
   }

   // Redraw everything
   m_pSite->Reformat();
   m_pSite->Invalidate();
}

void cChoice::onCreate()
{
   CComPtr< IPluginSite > pPluginSite;
   m_pSite->get_PluginSite( &pPluginSite );

   // Create the image info
   pPluginSite->LoadBitmapPortal( 0x06001276, &m_pInactive );
   BSTR bstrFontName;
   pPluginSite->get_FontName(&bstrFontName);
   pPluginSite->CreateFont( bstrFontName /*_bstr_t( _T( "Times New Roman" ) )*/, 14, 0, &m_pFont );

   // Create the dropdown
   CComObject< cChoiceDropDown > *pDropDown;
   CComObject< cChoiceDropDown >::CreateInstance( &pDropDown );

   pDropDown->m_pChoice = this;

   LayerParams lp = { 0, { -1, -1, 0, 0 }, 0 };
   m_pSite->CreateChild( &lp, pDropDown );

   m_pPopup = pDropDown->m_pSite;

   // Create the scroller
   CComPtr< ILayer > pScrollerLayer;
   HRESULT hRes = ::CoCreateInstance( CLSID_Scroller, NULL, CLSCTX_INPROC_SERVER, IID_ILayer, reinterpret_cast< void ** >( &pScrollerLayer ) );
   _ASSERTE( SUCCEEDED( hRes ) );
   pScrollerLayer->QueryInterface( &m_pScroller );

   // Create the scroller object
   LayerParams lpScroller = { 0, { 0, 0, 0, 0 }, eRenderClipped };
   m_pPopup->CreateChild( &lpScroller, pScrollerLayer );

   // Configure the scroller
   SIZE szLine = { 18, 1 };
   m_pScroller->put_Increments( &szLine );

   // Create the popup window
   CComObject< cChoicePopup > *pPopup;
   CComObject< cChoicePopup >::CreateInstance( &pPopup );

   pPopup->m_pChoice = this;
   m_pScroller->CreateClient( pPopup );

   m_pPopup->put_Transparent( VARIANT_FALSE );
   m_pSite->put_Transparent( VARIANT_FALSE );
}

void cChoice::onDestroy()
{
   m_pPopup.Release();

   m_pFont.Release();
   m_pInactive.Release();
}

STDMETHODIMP cChoice::MouseUp(MouseState *)
{
   m_bMouseDown = false;

   if( m_bMouseOver )
   {
      setPopup( !m_bPopup );

      if( !m_bPopup )
         m_pPopup->put_Popup( VARIANT_FALSE );
   }

   return S_OK;
}

#define RENDER_LEFTSTRETCH 10
#define RENDER_RIGHTSTRETCH 7
#define ROW_HEIGHT 18

#define ICON_WIDTH 20
#define ICON_HEIGHT 19

STDMETHODIMP cChoice::Render(ICanvas *pCanvas)
{
   // First draw the background
   RECT rcPos;
   m_pSite->get_Position( &rcPos );

   SIZE szImage;
   m_pInactive->get_Size( &szImage );

   static POINT ptBack = { 0, 0 };
   m_pInactive->StretchBlt( pCanvas, &ptBack, rcPos.right - rcPos.left, RENDER_LEFTSTRETCH, szImage.cx - RENDER_RIGHTSTRETCH );

   if( m_nSelected != -1 )
   {
      // Draw the selected text
      static POINT ptText = { 12, 2 };
      m_pFont->DrawTextEx( &ptText, m_options[ m_nSelected ].m_strText, 0, 0, eAA, pCanvas );
   }

   // Draw the up/down icon
   CComPtr< IPluginSite > pPluginSite;
   m_pSite->get_PluginSite( &pPluginSite );

   SIZE szIcon = { ICON_WIDTH, ICON_HEIGHT };
   CComPtr< IIconCache > pIconCache;
   pPluginSite->GetIconCache( &szIcon, &pIconCache );

   POINT ptIcon = { rcPos.right - rcPos.left - ICON_WIDTH, 0 };
   pIconCache->DrawIcon( &ptIcon, ( ( m_bMouseDown && m_bMouseOver ) ? !m_bPopup : m_bPopup ) ? 0x06001275 : 0x06001274, 0, pCanvas );

   return S_OK;
}

STDMETHODIMP cChoice::Reformat()
{
   if( !m_bPopup || m_options.size() == 0 )
   {
      // If it's hidden great, hide it well
      static RECT rcHidden = { -1, -1, 0, 0 };
      m_pPopup->put_Position( &rcHidden );

      return S_OK;
   }

   // Calculate the correct position
   RECT rc;
   m_pSite->get_ScreenPosition( &rc );

   // Two modes, with scroll bar and without
   if( m_options.size() > m_nDropLines )
   {
      // With a scroller
      RECT rcPopup = { rc.left, rc.bottom, rc.right - ICON_WIDTH + 16, rc.bottom +
         ROW_HEIGHT * m_nDropLines };
      m_pPopup->put_Position( &rcPopup );

      m_pScroller->put_VerticalEnabled( VARIANT_TRUE );

      SIZE szArea = { rc.right - ICON_WIDTH, ROW_HEIGHT * m_options.size() };
      m_pScroller->put_Area( &szArea );
   }
   else
   {
      RECT rcPopup = { rc.left, rc.bottom, rc.right - ICON_WIDTH, rc.bottom +
         ROW_HEIGHT * m_options.size() };
      m_pPopup->put_Position( &rcPopup );

      POINT ptOffset = { 0, 0 };
      m_pScroller->put_Offset( &ptOffset );
      m_pScroller->put_VerticalEnabled( VARIANT_FALSE );

      SIZE szArea = { rc.right - ICON_WIDTH, ROW_HEIGHT * m_options.size() };
      m_pScroller->put_Area( &szArea );
   }

   return S_OK;
}

STDMETHODIMP cChoice::SchemaLoad(IView *pView, IUnknown *pSchema)
{
   // Load the data from the schema

   MSXML::IXMLDOMElementPtr pElement = pSchema;

   MSXML::IXMLDOMElementPtr pRow;
   for( MSXML::IXMLDOMNodeListPtr pRows = pElement->selectNodes( _T( "option" ) );
      ( pRow = pRows->nextNode() ).GetInterfacePtr() != NULL; )
   {
      _variant_t vRow = pRow->getAttribute( _T( "text" ) ),
         vData = pRow->getAttribute( _T( "data" ) );
      _ASSERTE( vRow.vt == VT_BSTR );

      cOption o;
      o.m_strText = vRow;
      o.m_value = vData;

      m_options.push_back( o );
   }

   _variant_t vDropRows = pElement->getAttribute( "droplines" ),
      vSelected = pElement->getAttribute( "selected" );

   if( vDropRows.vt != VT_NULL )
   {
      try
      {
         m_nDropLines = static_cast< long >( vDropRows );
         _ASSERTE( m_nDropLines > 0 );
      }
      catch( ... )
      {
         // Type conversion error
         _ASSERTE( FALSE );
      }
   }

   if( vSelected.vt != VT_NULL )
   {
      try
      {
         m_nSelected = static_cast< long >( vSelected );
         _ASSERTE( m_nSelected >= 0 );
      }
      catch( ... )
      {
         // Type conversion error
         _ASSERTE( FALSE );
      }
   }

   return S_OK;
}

STDMETHODIMP cChoice::AddChoice(BSTR strDisplay, VARIANT vData)
{
   cOption o;
   o.m_strText = strDisplay;
   o.m_value = vData;

   m_options.push_back( o );
   m_pSite->Reformat();

	return S_OK;
}

STDMETHODIMP cChoice::get_ChoiceCount(long *pVal)
{
   _ASSERTE( pVal != NULL );

   *pVal = m_options.size();

	return S_OK;
}

STDMETHODIMP cChoice::get_Data(long nIndex, VARIANT *pVal)
{
	if( pVal == NULL )
		return E_POINTER;

	if( nIndex < 0 || nIndex > m_options.size() )
	{
        pVal->lVal = -1;
		pVal->vt = VT_I4;
	}

	else
	   ::VariantCopy( pVal, &m_options[ nIndex ].m_value );

	return S_OK;
}

STDMETHODIMP cChoice::put_Data(long nIndex, VARIANT newVal)
{
   _ASSERTE( nIndex >= 0 );
   _ASSERTE( nIndex < m_options.size() );

   ::VariantCopy( &m_options[ nIndex ].m_value, &newVal );

	return S_OK;
}

STDMETHODIMP cChoice::get_Text(long nIndex, BSTR *pVal)
{
   USES_CONVERSION;

   _ASSERTE( nIndex >= 0 );
   _ASSERTE( nIndex < m_options.size() );
   _ASSERTE( pVal != NULL );

   *pVal = OLE2BSTR( m_options[ nIndex ].m_strText );

	return S_OK;
}

STDMETHODIMP cChoice::put_Text(long nIndex, BSTR newVal)
{
   _ASSERTE( nIndex >= 0 );
   _ASSERTE( nIndex < m_options.size() );
   _ASSERTE( newVal != NULL );

   m_options[ nIndex ].m_strText = newVal;
   m_pPopup->Invalidate();

	return S_OK;
}

STDMETHODIMP cChoice::RemoveChoice(long nIndex)
{
   _ASSERTE( nIndex >= 0 );
   _ASSERTE( nIndex < m_options.size() );

   m_options.erase( m_options.begin() + nIndex );

   if( m_nSelected >= m_options.size() )
      m_nSelected = m_options.size() - 1;

   long nID;
   m_pSite->get_ID( &nID );
   Fire_Change( nID, m_nSelected );

   // Redraw the concerned
   m_pSite->Reformat();
   m_pPopup->Invalidate();

	return S_OK;
}

STDMETHODIMP cChoice::get_Dropped(VARIANT_BOOL *pVal)
{
   _ASSERTE( pVal != NULL );
   
   *pVal = ( m_bPopup ) ? VARIANT_TRUE : VARIANT_FALSE;

	return S_OK;
}

STDMETHODIMP cChoice::put_Dropped(VARIANT_BOOL newVal)
{
   if( !!newVal != m_bPopup )
      setPopup( !!newVal );

	return S_OK;
}

STDMETHODIMP cChoice::get_Selected(long *pVal)
{
   _ASSERTE( pVal != NULL );
   *pVal = m_nSelected;

	return S_OK;
}

STDMETHODIMP cChoice::put_Selected(long newVal)
{
   _ASSERTE( newVal >= -1 );
   //_ASSERTE( newVal < m_options.size() );
	//back out if out of bounds, don't error to prevent breaking vb plugs
	//this should fix the crash on display bug
	if (newVal >= m_options.size())
		return S_OK;

   if( newVal == m_nSelected )
      return S_OK;

   m_nSelected = newVal;

   long nID;
   m_pSite->get_ID( &nID );
   Fire_Change( nID, m_nSelected );

   // Redraw the concerned
   m_pSite->Invalidate();
   m_pPopup->Invalidate();

	return S_OK;
}

STDMETHODIMP cChoice::get_DropLines(long *pVal)
{
   _ASSERTE( pVal != NULL );

   *pVal = m_nDropLines;

	return S_OK;
}

STDMETHODIMP cChoice::put_DropLines(long newVal)
{
   _ASSERTE( newVal > 0 );

   m_nDropLines = newVal;
   m_pSite->Reformat();

	return S_OK;
}

STDMETHODIMP cChoice::Clear()
{
   m_options.clear();
   m_nSelected = -1;

   long nID;
   m_pSite->get_ID( &nID );
   Fire_Change( nID, m_nSelected );

   m_pSite->Reformat();

	return S_OK;
}
