// AdFile.cpp: implementation of the CAdFile class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#undef max
#include "AdRot.h"
#include "AdFile.h"
#include "RotObj.h"
#include "sinstrm.h"

extern CMonitor* g_pMonitor;

#ifdef DBG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//--------------------------------------------------------------------
//  CAdFileNotify
//--------------------------------------------------------------------
CAdFileNotify::CAdFileNotify()
    :   m_isNotified(0)
{
}

void
CAdFileNotify::Notify()
{
    ::InterlockedExchange( &m_isNotified, 1 );
}

bool
CAdFileNotify::IsNotified()
{
    return ( ::InterlockedExchange( &m_isNotified, 0 ) ? true : false );
}


//--------------------------------------------------------------------
//  CAdFile
//--------------------------------------------------------------------

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CAdFile::CAdFile()
    :   m_nBorder(defaultBorder),
        m_nHeight(defaultHeight),
        m_nWidth(defaultWidth),
        m_nVSpace(defaultVSpace),
        m_nHSpace(defaultHSpace),
        m_strRedirector( _T("")),
        m_fUTF8(false)
{
    m_pNotify = new CAdFileNotify;
}

CAdFile::~CAdFile()
{
    if ( g_pMonitor )
    {
        g_pMonitor->StopMonitoringFile( m_strFile.c_str() );
    }
}

//---------------------------------------------------------------------------
//
//  ProcessAdFile will check the given filename, if it matches the one it
//  knows it currently has in memory, it will do nothing.  If the filename
//  differs, the old ad information will be dumped, and the new information
//  parsed and stored.
//
//---------------------------------------------------------------------------
bool
CAdFile::ProcessAdFile(
    String  strAdFile )
{
    USES_CONVERSION;

    bool rc = false;
    UINT    weightSum = 0;
        
    // block all other readers and writers
    CWriter wtr( *this );
    m_ads.erase( m_ads.begin(), m_ads.end() );
    
    
    // parse the file
    FileInStream fs;
    HRESULT hr = fs.Init( T2CA(strAdFile.c_str()) );
    if ( SUCCEEDED(hr) && fs.is_open())
    {       
        if ( ReadHeader( fs ) )
        {
            while ( !fs.eof() )
            {
                // read one "ad record"
                String strGif;
                String strLink;
                String strAlt;
                String strWeight;
                ULONG lWeight = 0;
                
                fs >> strGif >> strLink;
                // this just gets us past the new line
                fs.readLine( strAlt );
                fs >> strWeight;
                               
                
                // check for a negative impression value.  RaiseException if
                // negative

                if (strWeight[0] == '-') {
                    CAdRotator::RaiseException( IDS_ERROR_BAD_WEIGHT_VALUE );
                    goto err;
                }
                lWeight = strWeight.toUInt32();

                weightSum += lWeight;

                if (weightSum > 10000) {
                    CAdRotator::RaiseException( IDS_ERROR_WEIGHT_SUM_TOO_LARGE );
                    goto err;
                }
                
                if ( lWeight != 0 )
                {
                    CAdDescPtr pAd = new CAdDescriptor( lWeight, strLink, strGif, strAlt );
                    if ( pAd.IsValid() )
                    {
                        // add one reference to the ad for each weight
                        for( int i = 0; i < lWeight; i++ )
                        {
                            m_ads.push_back( pAd );
                        }
                    }
                }
            }
            if ( m_ads.size() > 0 )
            {
                if ( m_strFile != strAdFile )
                {
                    g_pMonitor->StopMonitoringFile( m_strFile.c_str() );
                    m_strFile = strAdFile;
                    g_pMonitor->MonitorFile( m_strFile.c_str(), m_pNotify );
                }
                rc = true;
            }
        }
        else
        {
            CAdRotator::RaiseException( IDS_ERROR_CANNOT_READ_ROTATION_SCHEDULE_FILE );
        }
    }
    else
    {
        CAdRotator::RaiseException( IDS_ERROR_CANNOT_LOAD_ROTATION_SCHEDULE_FILE );
    }
err:
    
    return rc;
}

//---------------------------------------------------------------------------
//
//  Refresh will check to see if the cached information is out of date, if so
//  it will re-read the file
//
//---------------------------------------------------------------------------
bool
CAdFile::Refresh()
{
    bool rc = false;
    if ( m_pNotify->IsNotified() )
    {
        rc = ProcessAdFile( m_strFile );
    }
    return rc;
}


//---------------------------------------------------------------------------
//
//  ReadHeader will parse the header portion of the file.  The header includes
//  some or all of the following fields: HEIGHT, WIDTH, BORDER, REDIRECT,
//  HSPACE, VSPACE.  The fields are separated by newlines and the header is
//  terminated by an asterix.
//
//---------------------------------------------------------------------------
bool
CAdFile::ReadHeader(
    FileInStream&   fs )
{
    bool rc = false;

    // set defaults
    m_nHeight = defaultHeight;
    m_nWidth = defaultWidth;
    m_nHSpace = defaultHSpace;
    m_nVSpace = defaultVSpace;

    m_fUTF8 = fs.is_UTF8();

    bool done = false;
    while ( !fs.eof() && !done )
    {
        String strLine;
        String strName;
        fs.readLine( strLine );
        StringInStream sis( strLine );
        sis >> strName;
        HRESULT hr = S_OK;
        if ( _tcsicmp( strName.c_str(), _T("HEIGHT") ) == 0 )
        {
            hr = sis.readInt( m_nHeight );
        }
        else if ( _tcsicmp( strName.c_str(), _T("WIDTH") ) == 0 )
        {
            hr = sis.readInt( m_nWidth );
        }
        else if ( _tcsicmp( strName.c_str(), _T("VSPACE") ) == 0 )
        {
            hr = sis.readInt( m_nVSpace );
        }
        else if ( _tcsicmp( strName.c_str(), _T("HSPACE") ) == 0 )
        {
            hr = sis.readInt( m_nHSpace );
        }
        else if ( _tcsicmp( strName.c_str(), _T("REDIRECT") ) == 0 )
        {
            hr = sis.readString( m_strRedirector );
        }
        else if ( _tcsicmp( strName.c_str(), _T("BORDER" ) ) == 0 )
        {
            hr = sis.readInt16( m_nBorder );
        }
        else if ( _tcsicmp( strName.c_str(), _T("*") ) == 0 )
        {
            rc = true;
            done = true;
        }
        else
        {
            CAdRotator::RaiseException( IDS_ERROR_UNKNOWN_HEADER_NAME );
        }
/*
        if ( hr != S_OK )
        {
            CAdRotator::RaiseException( IDS_ERROR_HEADER_HAS_NO_ASSOCIATED_VALUE );
        }
*/      
    }
    return rc;
}

//---------------------------------------------------------------------------
//
//  RandomAd chooses and ad at random from the list of ads.  Since there
//  are multiple references to ads based on the weight, we need only produce
//  a random number between 0 and one less than the size of the list.
//
//---------------------------------------------------------------------------
CAdDescPtr
CAdFile::RandomAd() const
{
    if (m_ads.size() > 0)
        return m_ads[ rand() % m_ads.size() ];
    else
        return NULL;
}