/*
 * FAAC - Freeware Advanced Audio Coder
 * Copyright (C) 2001 Menno Bakker
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */

#include <windows.h>
#include <commdlg.h>
#include <commctrl.h>
#include <stdlib.h>

#include "input.h"

#include <faac.h>
#include "resource.h"


static HINSTANCE hInstance;

static char inputFilename[_MAX_PATH], outputFilename[_MAX_PATH];

static BOOL Encoding = FALSE;

static BOOL SelectFileName(HWND hParent, char *filename, BOOL forReading)
{
    OPENFILENAME ofn;

    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.hwndOwner = hParent;
    ofn.hInstance = hInstance;
    ofn.nFilterIndex = 0;
    ofn.lpstrFileTitle = NULL;
    ofn.nMaxFileTitle = 31;
    filename [0] = 0x00;
    ofn.lpstrFile = (LPSTR)filename;
    ofn.nMaxFile = _MAX_PATH;
    ofn.lpstrInitialDir = NULL;
    ofn.lpstrCustomFilter = NULL;
    ofn.nMaxCustFilter = 0;
    ofn.nFileOffset = 0;
    ofn.nFileExtension = 0;
    ofn.lCustData = 0;
    ofn.lpfnHook = NULL;
    ofn.lpTemplateName = NULL;

    if (forReading)
    {
        char filters[] = { "Wave Files (*.wav)\0*.wav\0" \
            "AIFF Files (*.aif;*.aiff;*.aifc)\0*.aif;*.aiff;*.aifc\0" \
            "AU Files (*.au)\0*.au\0" \
            "All Files (*.*)\0*.*\0\0" };

        ofn.lpstrFilter = filters;
        ofn.lpstrDefExt = "wav";

        ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
        ofn.lpstrTitle = "Select Source File";

        return GetOpenFileName (&ofn);
    } else {
        char filters [] = { "AAC Files (*.aac)\0*.aac\0" \
            "All Files (*.*)\0*.*\0\0" };

        ofn.lpstrFilter = filters;
        ofn.lpstrDefExt = "aac";

        ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY;
        ofn.lpstrTitle = "Select Output File";

        return GetSaveFileName(&ofn);
    }
}

static void AwakeDialogControls(HWND hWnd)
{
    char szTemp[64];
    pcmfile_t *infile = NULL;
    unsigned int sampleRate, numChannels;
    char *pExt;

    if ((infile = wav_open_read(inputFilename, 0)) == NULL)
        return;

    /* determine input file parameters */
    sampleRate = infile->samplerate;
    numChannels = infile->channels;

    wav_close(infile);

    SetDlgItemText (hWnd, IDC_INPUTFILENAME, inputFilename);

    strncpy(outputFilename, inputFilename, sizeof(outputFilename) - 5);
    outputFilename[sizeof(outputFilename) - 5] = '\0';

    pExt = strrchr(outputFilename, '.');

    if (pExt == NULL) lstrcat(outputFilename, ".aac");
    else lstrcpy(pExt, ".aac");

    EnableWindow(GetDlgItem(hWnd, IDC_OUTPUTFILENAME), TRUE);
    EnableWindow(GetDlgItem(hWnd, IDC_SELECT_OUTPUTFILE), TRUE);

    SetDlgItemText(hWnd, IDC_OUTPUTFILENAME, outputFilename);

    wsprintf(szTemp, "%iHz %ich", sampleRate, numChannels);
    SetDlgItemText(hWnd, IDC_INPUTPARAMS, szTemp);

    EnableWindow(GetDlgItem(hWnd, IDOK), TRUE);
}

static DWORD WINAPI EncodeFile(LPVOID pParam)
{
    HWND hWnd = (HWND) pParam;
    pcmfile_t *infile = NULL;

    GetDlgItemText(hWnd, IDC_INPUTFILENAME, inputFilename, sizeof(inputFilename));
    GetDlgItemText(hWnd, IDC_OUTPUTFILENAME, outputFilename, sizeof(outputFilename));

    /* open the input file */
    if ((infile = wav_open_read(inputFilename, 0)) != NULL)
    {
        /* determine input file parameters */
        unsigned int sampleRate = infile->samplerate;
        unsigned int numChannels = infile->channels;

        unsigned long inputSamples;
        unsigned long maxOutputBytes;

        /* set up parameters and open the encoder */
        faac_params params;
        faac_encoder *hEncoder = NULL;
	char szTemp[256];

        faac_params_init(&params);
        params.sample_rate  = sampleRate;
        params.num_channels = numChannels;
        params.input_format = FAAC_INPUT_32BIT;   /* wav_read_int24 -> 24-in-32 int */
        {
            HWND hOT = GetDlgItem(hWnd, IDC_OBJECTTYPE);
            LRESULT sel  = SendMessage(hOT, CB_GETCURSEL, 0, 0);
            LRESULT data = (sel != CB_ERR) ? SendMessage(hOT, CB_GETITEMDATA, (WPARAM)sel, 0) : CB_ERR;
            params.object_type = (data != CB_ERR) ? (enum faac_object_type)data : FAAC_OBJ_AUTO;
        }
        {
            LRESULT mode = SendMessage(GetDlgItem(hWnd, IDC_JOINTMODE), CB_GETCURSEL, 0, 0);
            params.joint_mode = (mode == CB_ERR) ? FAAC_JOINT_MIXED : (enum faac_joint_mode)mode;
        }
        params.use_tns = IsDlgButtonChecked(hWnd, IDC_USETNS) == BST_CHECKED;
        params.use_lfe = IsDlgButtonChecked(hWnd, IDC_USELFE) == BST_CHECKED;
        params.output_format = IsDlgButtonChecked(hWnd, IDC_USERAW) == BST_CHECKED
                             ? FAAC_STREAM_RAW : FAAC_STREAM_ADTS;
        params.mpeg_version = (enum faac_mpeg_version)
            SendMessage(GetDlgItem(hWnd, IDC_MPEGVERSION), CB_GETCURSEL, 0, 0);

        GetDlgItemText(hWnd, IDC_QUALITY, szTemp, sizeof(szTemp));
	params.quant_quality = atoi(szTemp);
	params.bit_rate = 0;  /* quality-driven; no bitrate control in this dialog */
	if (IsDlgButtonChecked(hWnd, IDC_BWCTL) == BST_CHECKED)
	{
            GetDlgItemText(hWnd, IDC_BANDWIDTH, szTemp, sizeof(szTemp));
            params.bandwidth = atoi(szTemp);
	}

        if (faac_encoder_open(&params, &hEncoder) == FAAC_OK)
        {
            HANDLE hOutfile;

            faac_encoder_info info;
            info.struct_size = sizeof(info);
            faac_encoder_get_info(hEncoder, &info);

            inputSamples   = (unsigned long)info.frame_samples * numChannels;
            maxOutputBytes = info.max_output_bytes;

	    sprintf(szTemp, "%u", info.quant_quality);
	    SetDlgItemText(hWnd, IDC_QUALITY, szTemp);

	    sprintf(szTemp, "%u", info.bandwidth);
	    SetDlgItemText(hWnd, IDC_BANDWIDTH, szTemp);

            /* open the output file */
            hOutfile = CreateFile(outputFilename, GENERIC_WRITE, 0, NULL,
                CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

            if (hOutfile != INVALID_HANDLE_VALUE)
            {
                UINT startTime = GetTickCount(), lastUpdated = 50;
                DWORD totalBytesRead = 0;

                unsigned int bytesInput = 0;
                DWORD numberOfBytesWritten = 0;
                int *pcmbuf;
                unsigned char *bitbuf;
                char HeaderText[50];
                char Percentage[5];

                pcmbuf = (int*)LocalAlloc(0, inputSamples*sizeof(int));
                bitbuf = (unsigned char*)LocalAlloc(0, maxOutputBytes*sizeof(unsigned char));

                SendDlgItemMessage(hWnd, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, 1024));
                SendDlgItemMessage(hWnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);

                for ( ;; )
                {
                    int bytesWritten;
                    UINT timeElapsed, timeEncoded;

                    bytesInput = wav_read_int24(infile, pcmbuf, inputSamples, NULL) * sizeof(int);

                    SendDlgItemMessage (hWnd, IDC_PROGRESS, PBM_SETPOS, (unsigned long)((float)totalBytesRead * 1024.0f / (infile->samples*sizeof(int)*numChannels)), 0);

                    /* Percentage for Dialog Output */
                    _itoa((int)((float)totalBytesRead * 100.0f / (infile->samples*sizeof(int)*numChannels)),Percentage,10);
                    lstrcpy(HeaderText,"FAAC GUI: ");
                    lstrcat(HeaderText,Percentage);
                    lstrcat(HeaderText,"%");
                    SendMessage(hWnd,WM_SETTEXT,0,(LPARAM)HeaderText);

                    totalBytesRead += bytesInput;

                    timeElapsed = (GetTickCount () - startTime) / 10;
                    timeEncoded = 100.0 * totalBytesRead / (sampleRate * numChannels * sizeof (int));

                    if (timeElapsed > (lastUpdated + 20))
                    {
                        float factor;
			unsigned timeLeft;

                        lastUpdated = timeElapsed;

                        factor = (float) timeEncoded / (float) (timeElapsed ? timeElapsed : 1);
			timeLeft = 10.0 * infile->samples / sampleRate / factor - 0.1 * timeElapsed;

			sprintf(szTemp, "Playing time: %2.2i:%04.1f\tEncoding time: %2.2i:%04.1f\n"
				"Play/enc factor: %.2f\tEstimated time left: %2.2i:%04.1f",
				timeEncoded / 6000, 0.01 * (timeEncoded % 6000),
				timeElapsed / 6000, 0.01 * (timeElapsed % 6000),
				factor,
				timeLeft / 600, 0.1 * (timeLeft % 600)
			       );

                        SetDlgItemText(hWnd, IDC_TIME, szTemp);
                    }

                    /* call the actual encoding routine */
                    {
                        uint32_t nbytes = 0;
                        faac_status st = faac_encoder_encode(hEncoder,
                            pcmbuf,
                            (uint32_t)(bytesInput/sizeof(int)),
                            bitbuf,
                            (uint32_t)maxOutputBytes,
                            &nbytes);
                        bytesWritten = (st == FAAC_OK) ? (int)nbytes : -1;
                    }

                    /* Stop Pressed */
                    if ( !Encoding )
                        break;

                    /* all done, bail out */
                    if (!bytesInput && !bytesWritten)
                        break;

                    if (bytesWritten < 0)
                    {
                        MessageBox (hWnd, "faac_encoder_encode failed!", "Error", MB_OK | MB_ICONSTOP);
                        break;
                    }

                    WriteFile(hOutfile, bitbuf, bytesWritten, &numberOfBytesWritten, NULL);

                    }

                CloseHandle(hOutfile);
                if (pcmbuf) LocalFree(pcmbuf);
                if (bitbuf) LocalFree(bitbuf);
            }

            faac_encoder_close(&hEncoder);
        }

        wav_close(infile);
        MessageBeep(1);

        SendDlgItemMessage(hWnd, IDC_PROGRESS, PBM_SETPOS, 0, 0);
    } else {
        MessageBox(hWnd, "Couldn't open input file!", "Error", MB_OK | MB_ICONSTOP);
    }

    SendMessage(hWnd,WM_SETTEXT,0,(LPARAM)"FAAC GUI");
    Encoding = FALSE;
    SetDlgItemText(hWnd, IDOK, "Encode");
    return 0;
}

static BOOL WINAPI DialogProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_INITDIALOG:
      {
	faac_library_info libinfo = { .struct_size = sizeof(libinfo) };
	char txt[100];
	faac_get_library_info(&libinfo);
	sprintf(txt, "libfaac version %s", libinfo.version ? libinfo.version : "?");
	SetDlgItemText(hWnd, IDC_COMPILEDATE, txt);
      }

        inputFilename[0] = 0x00;

        SendMessage(GetDlgItem(hWnd, IDC_MPEGVERSION), CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"MPEG4");
        SendMessage(GetDlgItem(hWnd, IDC_MPEGVERSION), CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"MPEG2");
        SendMessage(GetDlgItem(hWnd, IDC_MPEGVERSION), CB_SETCURSEL, 0, 0);

        {
            HWND hOT = GetDlgItem(hWnd, IDC_OBJECTTYPE);
            LRESULT idx;
            idx = SendMessage(hOT, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"Auto");
            SendMessage(hOT, CB_SETITEMDATA, idx, (LPARAM)FAAC_OBJ_AUTO);
            idx = SendMessage(hOT, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"Low Complexity");
            SendMessage(hOT, CB_SETITEMDATA, idx, (LPARAM)FAAC_OBJ_LOW);
            idx = SendMessage(hOT, CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"HE-AAC v1");
            SendMessage(hOT, CB_SETITEMDATA, idx, (LPARAM)FAAC_OBJ_HE_AAC_V1);
            SendMessage(hOT, CB_SETCURSEL, 0, 0);
        }

        SendMessage(GetDlgItem(hWnd, IDC_JOINTMODE), CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"None");
        SendMessage(GetDlgItem(hWnd, IDC_JOINTMODE), CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"M/S");
        SendMessage(GetDlgItem(hWnd, IDC_JOINTMODE), CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"IS");
        SendMessage(GetDlgItem(hWnd, IDC_JOINTMODE), CB_ADDSTRING, 0, (LPARAM)(LPCTSTR)"Mixed");
        SendMessage(GetDlgItem(hWnd, IDC_JOINTMODE), CB_SETCURSEL, 3, 0);

        CheckDlgButton(hWnd, IDC_USELFE, FALSE);
        CheckDlgButton(hWnd, IDC_USERAW, FALSE);
        CheckDlgButton(hWnd, IDC_USETNS, FALSE);
        SetDlgItemText(hWnd, IDC_QUALITY, "100");
        SetDlgItemText(hWnd, IDC_BANDWIDTH, "0");

        DragAcceptFiles(hWnd, TRUE);
        return TRUE;

    case WM_DROPFILES:

        if (DragQueryFile((HDROP) wParam, 0, (LPSTR) inputFilename, _MAX_PATH - 1))
            AwakeDialogControls(hWnd);

        DragFinish((HDROP) wParam);
        return FALSE;

    case WM_COMMAND:

        switch (wParam)
        {
        case IDOK:

            if ( !Encoding )
            {
                DWORD retval;
                CreateThread(NULL,0,EncodeFile,hWnd,0,&retval);
                Encoding = TRUE;
                SetDlgItemText(hWnd, IDOK, "Stop");
            }
            else
            {
                Encoding = FALSE;
                SetDlgItemText(hWnd, IDOK, "Encode");
            }
            return TRUE;

        case IDCANCEL:

            EndDialog(hWnd, TRUE);
            return TRUE;

        case IDC_SELECT_INPUTFILE:

            if (SelectFileName(hWnd, inputFilename, TRUE))
                AwakeDialogControls(hWnd);

            break;

        case IDC_SELECT_OUTPUTFILE:

            if (SelectFileName(hWnd, outputFilename, FALSE))
            {
                SetDlgItemText(hWnd, IDC_OUTPUTFILENAME, outputFilename);
            }

            break;
	case IDC_BWCTL:
	  switch (IsDlgButtonChecked(hWnd, IDC_BWCTL))
	  {
	  case BST_CHECKED:
	    EnableWindow(GetDlgItem(hWnd, IDC_BANDWIDTH), TRUE);
	    //SetDlgItemText(hWnd, IDC_BANDWIDTH, "0");
            break;
	  case BST_UNCHECKED:
	    EnableWindow(GetDlgItem(hWnd, IDC_BANDWIDTH), FALSE);
	    //SetDlgItemText(hWnd, IDC_BANDWIDTH, "");
            break;
	  }
	  break;

        case MAKEWPARAM(IDC_OBJECTTYPE, CBN_SELCHANGE):
        {
            HWND hOT  = GetDlgItem(hWnd, IDC_OBJECTTYPE);
            HWND hMPG = GetDlgItem(hWnd, IDC_MPEGVERSION);
            LRESULT sel  = SendMessage(hOT, CB_GETCURSEL, 0, 0);
            LRESULT data = (sel != CB_ERR) ? SendMessage(hOT, CB_GETITEMDATA, (WPARAM)sel, 0) : CB_ERR;
            if (data == (LRESULT)FAAC_OBJ_HE_AAC_V1) {
                SendMessage(hMPG, CB_SETCURSEL, 0, 0);
                EnableWindow(hMPG, FALSE);
            } else {
                EnableWindow(hMPG, TRUE);
            }
            break;
        }
        }

        break;
    }

    return FALSE;
}

int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    hInstance = hInst;

    return DialogBox(hInstance, MAKEINTRESOURCE (IDD_MAINDIALOG), NULL, (DLGPROC) DialogProc);
}
