// Document.cpp: implementation of the CDocument class.
//
//////////////////////////////////////////////////////////////////////


#define _WIN32_WINNT 0x0400
#define _ATL_APARTMENT_THREADED

#include "vcproj.h"
extern TCHAR g_szStart[];
extern TCHAR g_szEnd[];
extern TCHAR g_szAll[];
extern char szAppName[];
LPTSTR   GetStringRes (int id);

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


CDocument::~CDocument()
{

}


BOOL CDocument::Initialize(HWND hwndDoc)
{
    m_hWnd = hwndDoc;
    return m_ClipList.Initialize(hwndDoc);
}

void inline WriteLong(IStream *pStream, long val)
{
    pStream->Write(&val, sizeof(val), NULL);
}

long inline ReadLong(IStream *pStream)
{
    long val;
    pStream->Read(&val, sizeof(val), NULL);
    return val;
}

void CDocument::ResetContents(void)
{
    m_TargetFileName.Empty();
    m_VideoCodecDisplayName.Empty();
    m_AudioCodecDisplayName.Empty();
    m_ClipList.ResetContents();
}

BOOL CDocument::ReadFromStream(IStream * pStream)
{
    ResetContents();
      
    m_TargetFileName.ReadFromStream(pStream);
    m_VideoCodecDisplayName.ReadFromStream(pStream);
    m_AudioCodecDisplayName.ReadFromStream(pStream);
    m_Height = ReadLong(pStream);
    m_Width = ReadLong(pStream);
    m_PixelDepth = ReadLong(pStream);

    m_ClipList.ReadFromStream(pStream);
    return TRUE;
}

BOOL CDocument::WriteToStream(IStream * pStream)
{
    m_TargetFileName.WriteToStream(pStream);
    m_VideoCodecDisplayName.WriteToStream(pStream);
    m_AudioCodecDisplayName.WriteToStream(pStream);
    WriteLong(pStream, m_Height);
    WriteLong(pStream, m_Width);
    WriteLong(pStream, m_PixelDepth);
    m_ClipList.WriteToStream(pStream);
    return TRUE;
}


BOOL CDocument::NewClip()
{
    CClip *pClip;
    int i = m_ClipList.CurSelClipIndex();
    if (i < 0) {    // Nothing selected = -1;  Add to end.
        i = m_ClipList.NumClips();
    }
    m_ClipList.AddClip(i, &pClip);
    if (pClip->DoSettingsDialog(m_hWnd)) {
        m_ClipList.UpdateClipView(i);
        MarkDirty();
    } else {
        m_ClipList.DeleteClip(i);
    }
    return TRUE;
}

BOOL CDocument::DeleteClip()
{
    int i = m_ClipList.CurSelClipIndex();
    if (i >= 0) {
        m_ClipList.DeleteClip(i);
        MarkDirty();
    }
    return TRUE;
}

BOOL CDocument::EditClip()
{
    int i = m_ClipList.CurSelClipIndex();
    if (i >= 0) {
        CClip *pClip = m_ClipList.GetClip(i);
        if (pClip->DoSettingsDialog(m_hWnd)) {
            m_ClipList.UpdateClipView(i);
            MarkDirty();
        }
    }
    return TRUE;
}

extern HINSTANCE hInst;



void InitOpenFileName(HWND hWnd, OPENFILENAME& OpenFileName, TCHAR * pszFileName)
{
    OpenFileName.lStructSize       = sizeof(OPENFILENAME);
    OpenFileName.hwndOwner         = hWnd;
    OpenFileName.hInstance         = hInst;
    OpenFileName.lpstrFilter       = NULL;
    OpenFileName.lpstrCustomFilter = NULL;
    OpenFileName.nMaxCustFilter    = 0;
    OpenFileName.nFilterIndex      = 0;
    OpenFileName.lpstrFile         = pszFileName;
    OpenFileName.nMaxFile          = MAX_PATH;
    OpenFileName.lpstrFileTitle    = NULL;
    OpenFileName.nMaxFileTitle     = 0;
    OpenFileName.lpstrInitialDir   = NULL;
    OpenFileName.lpstrTitle        = NULL;
    OpenFileName.nFileOffset       = 0;
    OpenFileName.nFileExtension    = 0;
    OpenFileName.lpstrDefExt       = NULL;
    OpenFileName.lCustData         = 0;
    OpenFileName.lpfnHook 	   = NULL;
    OpenFileName.lpTemplateName    = NULL;
    OpenFileName.Flags             = OFN_SHOWHELP;
}


BOOL CDocument::OpenFile()
{
    USES_CONVERSION;
    BOOL bWorked = FALSE;
    TCHAR szFile[MAX_PATH];

    szFile[0] = 0;
    OPENFILENAME OpenFileName;
    InitOpenFileName(m_hWnd, OpenFileName, szFile);

    if (GetOpenFileName(&OpenFileName)) {
        CComBSTR szTryThisName(T2BSTR(szFile));
        CComPtr<IStorage> pStorage;
        HRESULT hr = StgOpenStorage(szTryThisName, NULL,  
                                    STGM_READ | STGM_SHARE_EXCLUSIVE, NULL, 0,
                                    &pStorage);
        if (SUCCEEDED(hr)) {
            CComPtr <IStream> pStream;
            hr = pStorage->OpenStream(L"VidClip", NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
            if (SUCCEEDED(hr)) {
                bWorked = ReadFromStream(pStream);
                if (bWorked) {
                    m_DocumentFileName = szTryThisName;
                }
            }
        }
    }
    return bWorked;
}


BOOL CDocument::SaveAsFile(bool bShowDialog)
{
    USES_CONVERSION;
    BOOL bWorked = FALSE;
    TCHAR szFile[MAX_PATH];

    if (!m_DocumentFileName || bShowDialog) {
        OPENFILENAME OpenFileName;
        InitOpenFileName(m_hWnd, OpenFileName, szFile);
        if (m_DocumentFileName) {
            lstrcpy(szFile, OLE2T(m_DocumentFileName));    
        } else {
            szFile[0] = 0;
        }
        if (!GetSaveFileName(&OpenFileName)) {
            return FALSE;
        }
        m_DocumentFileName = T2OLE(szFile);
    }

    CComPtr<IStorage> pStorage;
    HRESULT hr = StgCreateDocfile(m_DocumentFileName, 
                 STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, 0,
                 &pStorage);
    if (SUCCEEDED(hr)) {
        CComPtr<IStream> pStream;
        hr = pStorage->CreateStream(L"VidClip", STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream);
        if (SUCCEEDED(hr)) {
            bWorked = WriteToStream(pStream);
        }
    }
    return bWorked;
}



//////////////////// CLIPS


void inline WriteStreamTime(IStream *pStream, STREAM_TIME val)
{
    pStream->Write(&val, sizeof(val), NULL);
}

STREAM_TIME inline ReadStreamTime(IStream *pStream)
{
    STREAM_TIME val;
    pStream->Read(&val, sizeof(val), NULL);
    return val;
}



BOOL CClip::WriteToStream(IStream *pStream)
{
    m_FileName.WriteToStream(pStream);
    WriteStreamTime(pStream, m_stStart);
    WriteStreamTime(pStream, m_stEnd);
    return TRUE;
}

BOOL CClip::ReadFromStream(IStream *pStream)
{
    m_FileName.Empty();
    m_FileName.ReadFromStream(pStream);
    m_stStart = ReadStreamTime(pStream);
    m_stEnd = ReadStreamTime(pStream);
    return TRUE;
}

//////////////////// CLIP LIST

void CClipList::InsertCol(int iColNum, TCHAR * pszColHeader, int Width)
{
    LV_COLUMN col = {LVCF_FMT | LVCF_WIDTH | LVCF_TEXT, LVCFMT_LEFT, Width, pszColHeader, 0, 0};
    ListView_InsertColumn(m_hLV, iColNum, &col);
}

BOOL CClipList::Initialize(HWND hwndParent)
{
    m_hLV = CreateWindowEx(0, WC_LISTVIEW, "ClipList", WS_VISIBLE | WS_CHILDWINDOW | WS_CLIPSIBLINGS | LVS_REPORT | LVS_SINGLESEL,
                           0, 0, 0, 0, hwndParent, (HMENU)1, hInst, NULL);


    InsertCol(0, "File Name", 400);
    InsertCol(1, "Start", 100);
    InsertCol(2, "Stop", 100);
    return TRUE;
}


int CClipList::NumClips(void)
{
    return ListView_GetItemCount(m_hLV);
}

int CClipList::CurSelClipIndex(void)
{
    return ListView_GetNextItem(m_hLV, -1, LVNI_ALL | LVNI_SELECTED);
}

CClip * CClipList::GetClip(int i)
{
    LV_ITEM lvi = {LVIF_PARAM, i, 0};
    ListView_GetItem(m_hLV, &lvi);
    return (CClip *)(lvi.lParam);
}

BOOL CClipList::AddClip(int i, CClip **ppClip)
{
    CClip *pNewClip = new CClip();
    if (!pNewClip) {
        return FALSE;
    }
    LV_ITEM lvi = {LVIF_PARAM | LVIF_TEXT, i, 0};
    lvi.pszText = "<New Clip>";
    lvi.lParam = (LPARAM)pNewClip;
    ListView_InsertItem(m_hLV, &lvi);
    lvi.pszText = "----";
    lvi.mask = LVIF_TEXT;
    lvi.iSubItem = 1;
    ListView_SetItem(m_hLV, &lvi);
    lvi.iSubItem = 2;
    ListView_SetItem(m_hLV, &lvi);
    *ppClip = pNewClip;

    return TRUE;
}

void CClipList::DeleteClip(int i)
{
    delete GetClip(i);
    ListView_DeleteItem(m_hLV, i);
}


void CClipList::UpdateClipView(int i)
{
    USES_CONVERSION;
    CClip *pClip = GetClip(i);

    LV_ITEM lvi = {LVIF_TEXT, i, 0};
    lvi.pszText = OLE2T(pClip->m_FileName);
    ListView_SetItem(m_hLV, &lvi);


    if (pClip->m_stStart == 0 && pClip->m_stEnd == 0) {
        lvi.iSubItem = 1;
        lvi.pszText = g_szAll;
        ListView_SetItem(m_hLV, &lvi);
        lvi.iSubItem = 2;
        lvi.pszText = _T("");
        ListView_SetItem(m_hLV, &lvi);
    } else {
        TCHAR s[20];
        long Second = (long)(pClip->m_stStart / (STREAM_TIME)10000000);
        wsprintf(s, _T("%li"), Second);
        lvi.iSubItem = 1;
        lvi.pszText = pClip->m_stStart == 0 ? g_szStart : s;
        ListView_SetItem(m_hLV, &lvi);

        Second = (long)(pClip->m_stEnd / (STREAM_TIME)10000000);
        wsprintf(s, _T("%li"), Second);
        lvi.iSubItem = 2;
        lvi.pszText = pClip->m_stEnd == 0 ? g_szEnd : s;
        ListView_SetItem(m_hLV, &lvi);
    }
}



BOOL CClipList::WriteToStream(IStream *pStream)
{
    int lNumClips = NumClips();
    WriteLong(pStream, lNumClips);
    for (int i = 0; i < lNumClips; i++) {
        GetClip(i)->WriteToStream(pStream);
    }
    return TRUE;

}

void CClipList::ResetContents()
{
    int lNumClips = NumClips();
    for (int i = 0; i < lNumClips; i++) {
        delete GetClip(i);
    }
    ListView_DeleteAllItems(m_hLV);
}

BOOL CClipList::ReadFromStream(IStream *pStream)
{
    ResetContents();

    int lNumClips = ReadLong(pStream);
    for (int i = 0; i < lNumClips; i++) {
        CClip *pClip;
        AddClip(i, &pClip);
        pClip->ReadFromStream(pStream);
        UpdateClipView(i);
    }
    return TRUE;
}

void CClipList::SetSize(int Height, int Width)
{
    MoveWindow(m_hLV, 0, 0, Height, Width, TRUE);
}


LRESULT CALLBACK ClipSettingsDlg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);


BOOL CClip::DoSettingsDialog(HWND hwndParent)
{
    return DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_CLIP), hwndParent, (DLGPROC)ClipSettingsDlg, (LPARAM)this);
}


void inline SetEditSeconds(HWND hDlg, int ID, STREAM_TIME st)
{
    long Second = (long)(st / (STREAM_TIME)10000000);
    TCHAR s[20];
    wsprintf(s, _T("%li"), Second);
    SendDlgItemMessage(hDlg, ID, WM_SETTEXT, 0, (LPARAM)s);
}


void CClip::InitDialog(HWND hDlg)
{
    USES_CONVERSION; 
    CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
    SendDlgItemMessage(hDlg, IDC_EDIT_FILENAME, WM_SETTEXT, 0, (LPARAM)W2T(m_FileName));
    SetEditSeconds(hDlg, IDC_EDIT_START, m_stStart);
    SetEditSeconds(hDlg, IDC_EDIT_END, m_stEnd);
}


bool CClip::CleanUpDialog(HWND hDlg, bool bSaveSettings)
{
    bool bWorked = true;
    if (bSaveSettings) {
        TCHAR szEditContents[MAX_PATH];
        TCHAR *pUnused;
        STREAM_TIME stStart, stEnd;

        SendDlgItemMessage(hDlg, IDC_EDIT_START, WM_GETTEXT, sizeof(szEditContents) / sizeof(TCHAR), (LPARAM)szEditContents);
        stStart = (STREAM_TIME)strtol(szEditContents, &pUnused, 10) * (STREAM_TIME)10000000;

        SendDlgItemMessage(hDlg, IDC_EDIT_END, WM_GETTEXT, sizeof(szEditContents) / sizeof(TCHAR), (LPARAM)szEditContents);
        stEnd = (STREAM_TIME)strtol(szEditContents, &pUnused, 10) * (STREAM_TIME)10000000;

        SendDlgItemMessage(hDlg, IDC_EDIT_FILENAME, WM_GETTEXT, sizeof(szEditContents) / sizeof(TCHAR), (LPARAM)szEditContents);

        if (stStart <= stEnd || stEnd == 0) {
            HANDLE hTest = CreateFile(szEditContents, GENERIC_READ, FILE_SHARE_READ, NULL, 
                                      OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
            if (hTest == INVALID_HANDLE_VALUE) {
                bWorked = false;
                MessageBox(hDlg, GetStringRes(IDS_FILE_DOES_NOT_EXIST), szAppName, MB_OK);
            } else {
                CloseHandle(hTest);
                m_FileName = szEditContents;
                m_stStart = stStart;
                m_stEnd = stEnd;
            }
        } else {
            bWorked = false;
            MessageBox(hDlg, GetStringRes(IDS_INVALID_TIMES), szAppName, MB_OK);
        } 
    }
    return bWorked;
}

//
//  FUNCTION: ClipSettings(HWND, unsigned, WORD, LONG)
//
//
//  MESSAGES:
//
// WM_INITDIALOG - initialize dialog box
// WM_COMMAND    - Input received
//
//

LRESULT CALLBACK ClipSettingsDlg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    CClip *pClip = (CClip *)GetWindowLong(hDlg, DWL_USER);
    switch (message) {
        case WM_INITDIALOG:
            pClip = (CClip *)lParam;
            SetWindowLong(hDlg, DWL_USER, lParam);
            pClip->InitDialog(hDlg);
            return TRUE;

    

        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
                if (pClip->CleanUpDialog(hDlg, LOWORD(wParam) == IDOK)) {
                    EndDialog(hDlg, LOWORD(wParam) == IDOK);
                }
                return TRUE;
            }
            if (LOWORD(wParam) == IDC_BUTTON_BROWSE) {
                TCHAR szFile[MAX_PATH];
                szFile[0] = 0;
                OPENFILENAME OpenFileName;
                InitOpenFileName(hDlg, OpenFileName, szFile);
                if (GetOpenFileName(&OpenFileName)) {
                    SendDlgItemMessage(hDlg, IDC_EDIT_FILENAME, WM_SETTEXT, 0, (LPARAM)szFile);
                }
                return TRUE;
            }
            break;
   }

   return FALSE;
}
