#include <stdio.h>
#include <windows.h>
#include <assert.h>
//#include "msxplug.h"
#include "kssplay.h"
#include "optdlg/optdlg.h"
#include "rc/resource.h"

static const int XSTART = 2;//4;//TextOut 時の左端からの距離
static const int YSTART = 2;//4;//TextOut 時の上端からの距離
static const int MAX_FONTHEIGHT = 40;
static const int MIN_FONTHEIGHT = 16;
static wchar_t *getLyrics(OPTDLG *__this)
{
  int i, now;
  DWORD decode_pos;
  DWORD play_pos;
  __this->getLyrics(__this->instance, 
                    __this->lyrics_buf[__this->lyrics_pos], 
                    _countof(__this->lyrics_buf[0]), &decode_pos, &play_pos);
  __this->lyrics_buf_time[__this->lyrics_pos] = decode_pos;

  now = play_pos;
  for(i=__this->lyrics_pos;i!=(__this->lyrics_pos+1)%MAX_BUFFERED;i=(i+MAX_BUFFERED-1)%MAX_BUFFERED)
  {
    if(now+FRAME_PERIOD >= __this->lyrics_buf_time[i])
    break;
  }

  __this->lyrics_pos = (__this->lyrics_pos+1)%MAX_BUFFERED;
  return __this->lyrics_buf[i];
}
/*
static char *getLyrics(OPTDLG *__this)
{
  int i, now;

  strncpy(__this->lyrics_buf[__this->lyrics_pos], MSXPLUG_getMGStext(),80);
  __this->lyrics_buf_time[__this->lyrics_pos] = MSXPLUG_getDecodeTime();
  now = MSXPLUG_getOutputTime();

  for(i=__this->lyrics_pos;i!=(__this->lyrics_pos+1)%MAX_BUFFERED;i=(i+MAX_BUFFERED-1)%MAX_BUFFERED)
  {
    if(now+FRAME_PERIOD >= __this->lyrics_buf_time[i])
    break;
  }

  __this->lyrics_pos = (__this->lyrics_pos+1)%MAX_BUFFERED;
  return __this->lyrics_buf[i];
}
*/
static void GetWidthHeight(OPTDLG* __this, int *cx, int *cy, int *width, int *height)
{
    //最大文字幅は半角80文字分(SHIFT-JIS で 80 バイト)
    //データによっては 80 文字目が全角1バイト目になって実質 81 バイトになるものがある(imakoi.mgs)
    //82バイト使うものもある(rounin.mgs)
    //rounin.mgs の実機での表示はどうなるんだろうか…。
    //もしかして全角の「」(またはある種の記号)が半角で表示される？
    //一部の全角文字は半角に変換した方が良い？
    static const wchar_t szMaxText[] = //最大長の文字列(固定幅フォントなので80文字なら何でも良いはず)
    //            10        20        30        40        50        60        70        80
        L"1234567890123456789012345678901234567890123456789012345678901234567890123456789012";//82バイト(念の為)
    SIZE size;
    GetTextExtentPoint32(__this->hDC, szMaxText, _countof(szMaxText)-1, &size);
    *cx = size.cx;
    *cy = size.cy;
    //*width = size.cx + GetSystemMetrics(SM_CXSIZEFRAME)*2+XSTART*2;
    //*height = size.cy + GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME/*SM_CYSIZEFRAME*/)*2 + YSTART*2;
    *width = size.cx + __this->width_offset + XSTART*2;
    *height = size.cy + __this->height_offset + YSTART*2;

    /*RECT rc1;
    RECT rc2;
    GetWindowRect(hWnd, &rc1);
    GetClientRect(hWnd, &rc2);

    *width = size.cx + (rc1.right - rc1.left) - (rc2.right - rc2.left) + XSTART*2;
    *height = size.cy + (rc1.bottom - rc1.top) - (rc2.bottom - rc2.top) + YSTART*2;
    * */
}
static void create_font(OPTDLG*__this, int nHeight, int nWidth, const wchar_t *cszFont)
{
    if(nHeight > MAX_FONTHEIGHT){
        nHeight = MAX_FONTHEIGHT;
    }
    else if(nHeight < MIN_FONTHEIGHT){
        nHeight = MIN_FONTHEIGHT;
    }
    if(__this->hFont)
        DeleteObject(__this->hFont);
    __this->hFont = CreateFont(
                    nHeight, nWidth, 0, 0, 
      0, 0, 0, 0, SHIFTJIS_CHARSET, 
      OUT_DEFAULT_PRECIS, 
      CLIP_DEFAULT_PRECIS, 
      DEFAULT_QUALITY, 
      FIXED_PITCH|FF_DONTCARE,
      cszFont);
    SetTextAlign(__this->hDC, TA_TOP);
    SelectObject(__this->hDC,(HGDIOBJ)__this->hFont);

    int cx, cy, width, height;
    GetWidthHeight(__this, &cx, &cy, &width, &height);

    if(__this->hBitmap){
        DeleteObject(__this->hBitmap);
    }
    __this->hBitmap = CreateCompatibleBitmap(__this->hDC, width, height);
    SelectObject(__this->hDC, (HGDIOBJ)__this->hBitmap);
    RECT rect;
    rect.left = GetSystemMetrics(SM_CXSIZEFRAME);
    rect.right = rect.left + cx;
    rect.top = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYSIZEFRAME);
    rect.bottom = rect.top + cy;
    FillRect(__this->hDC, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
}

static void restrict_sizing(WPARAM wParam, LPARAM lParam, int min_w, int min_h, int max_w, int max_h)
{
  LPRECT lpRect = (LPRECT)lParam;
  int width = lpRect->right - lpRect->left;
  int height = lpRect->bottom - lpRect->top;

  switch(wParam)
  {
  case WMSZ_BOTTOM:
    if(height>max_h)
      lpRect->bottom = lpRect->top + max_h;
    else if(height<min_h)
      lpRect->bottom = lpRect->top + min_h;
    break;

  case WMSZ_BOTTOMLEFT:
    if(height>max_h)
      lpRect->bottom = lpRect->top + max_h;
    else if(height<min_h)
      lpRect->bottom = lpRect->top + min_h;
    if(width>max_w)
      lpRect->left = lpRect->right - max_w;
    else if(width<min_w)
      lpRect->left = lpRect->right - min_w;
    break;

  case WMSZ_BOTTOMRIGHT:
    if(height>max_h)
      lpRect->bottom = lpRect->top + max_h;
    else if(height<min_h)
      lpRect->bottom = lpRect->top + min_h;
    if(width>max_w)
      lpRect->right = lpRect->left + max_w;
    else if(width<min_w)
      lpRect->right = lpRect->left + min_w;
    break;

  case WMSZ_LEFT:
    if(width>max_w)
      lpRect->left = lpRect->right - max_w;
    else if(width<min_w)
      lpRect->left = lpRect->right - min_w;
    break;

  case WMSZ_RIGHT:
    if(width>max_w)
      lpRect->right = lpRect->left + max_w;
    else if(width<min_w)
      lpRect->right = lpRect->left + min_w;
    break;

  case WMSZ_TOP:
    if(height>max_h)
      lpRect->top = lpRect->bottom - max_h;
    else if(height<min_h)
      lpRect->top = lpRect->bottom - min_h;
    break;

  case WMSZ_TOPLEFT:
    if(height>max_h)
      lpRect->top = lpRect->bottom - max_h;
    else if(height<min_h)
      lpRect->top = lpRect->bottom - min_h;
    if(width>max_w)
      lpRect->left = lpRect->right - max_w;
    else if(width<min_w)
      lpRect->left = lpRect->right - min_w;
    break;

  case WMSZ_TOPRIGHT:
    if(height>max_h)
      lpRect->top = lpRect->bottom - max_h;
    else if(height<min_h)
      lpRect->top = lpRect->bottom - min_h;
    if(width>max_w)
      lpRect->right = lpRect->left + max_w;
    else if(width<min_w)
      lpRect->right = lpRect->left + min_w;
    break;

  default:
    break;
  }
}

static void CheckSize(WPARAM wParam, LPARAM lParam,
                      double RX, double RY, int width_offset, int height_offset)
{
    if(RX == 0 || RY == 0){
        return;
    }
    RECT    *rc;
    double  width,height;
    rc = (RECT *)lParam;
    width  = rc->right - rc->left;
    height = rc->bottom - rc->top;

    int nHeightOffset = height_offset + YSTART*2;
    int nWidthOffset = width_offset + XSTART*2;
    width -= nWidthOffset;
    height -= nHeightOffset;
    switch(wParam){
        case WMSZ_TOP:
        case WMSZ_BOTTOM:
            rc->right = rc->left + (int)(height*(RX/RY)) + nWidthOffset;
            break;
        case WMSZ_LEFT:
        case WMSZ_RIGHT:
            //rc->right = rc->left + (int)(height*(RX/RY)) + nWidthOffset;
            rc->bottom = rc->top + (int)(width*(RY/RX)) + nHeightOffset;
            break;
        case WMSZ_TOPLEFT:
            if((width/height)<(RX/RY)){
                rc->left = rc->right - (int)(height*(RX/RY)) - nWidthOffset;
            }else{
                rc->top = rc->bottom - (int)(width*(RY/RX)) - nHeightOffset;
            }
            break;
        case WMSZ_TOPRIGHT:
            if((width/height)<(RX/RY)){
                rc->right = rc->left + (int)(height*(RX/RY)) + nWidthOffset;
            }else{
                rc->top = rc->bottom - (int)(width*(RY/RX)) - nHeightOffset;
            }
            break;
        case WMSZ_BOTTOMLEFT:
            if((width/height)<(RX/RY)){
                rc->left = rc->right - (int)(height*(RX/RY)) - nWidthOffset;
            }else{
                rc->bottom = rc->top + (int)(width*(RY/RX)) + nHeightOffset;
            }
            break;
        case WMSZ_BOTTOMRIGHT:
            if((width/height)<(RX/RY)){
                rc->right = rc->left + (int)(height*(RX/RY)) + nWidthOffset;
            }else{
                rc->bottom = rc->top + (int)(width*(RY/RX)) + nHeightOffset;
            }
            break;
    }
}
static INT_PTR CALLBACK dlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  OPTDLG *__this = (OPTDLG *)GetPropW(hDlg, L"OPT") ;
  wchar_t tmp[81];
  RECT rect, crect;
  PAINTSTRUCT ps;
  HDC hDC;

  switch (uMsg)
	{
  case WM_TIMER:{
	memset(tmp, '\0', sizeof(tmp));
    wcsncpy_s(tmp, getLyrics(__this), _TRUNCATE);
    GetClientRect(hDlg,&rect);
    
    FillRect(__this->hDC, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
    //80文字ギリギリ表示出来るかのテスト
    //wcsncpy_s(tmp, L"12345678901234567890123456789012345678901234567890123456789012345678901234567890", _TRUNCATE);
    TextOut(__this->hDC, XSTART, YSTART, tmp, wcslen(tmp));

    InvalidateRect(hDlg,NULL,FALSE);
    return TRUE;
  }
  case WM_PAINT:
    hDC = BeginPaint(hDlg,&ps);
    GetClientRect(hDlg, &rect);
    BitBlt(hDC, 0, 0, rect.right, rect.bottom, __this->hDC, 0, 0, SRCCOPY);
    EndPaint(hDlg,&ps);
    return TRUE;

  case WM_INITDIALOG:
		return TRUE;

  case WM_COMMAND:
    /*
    if(LOWORD(wParam) == IDM_CHOOSEFONT)
    {
      LOGFONT lf;
      CHOOSEFONT cf;

      memset(&cf, 0, sizeof(CHOOSEFONT));
      cf.lStructSize = sizeof(CHOOSEFONT);
      cf.hwndOwner = hDlg;
      cf.lpLogFont = &lf;
      cf.Flags = CF_SCREENFONTS | CF_EFFECTS;
      cf.rgbColors = RGB(0, 0, 0);
      cf.nFontType = SCREEN_FONTTYPE;
      if(ChooseFont(&cf))
      {
        if(__this->hFont)
          DeleteObject((HGDIOBJ)__this->hFont);
        __this->hFont = CreateFontIndirect(&lf);
        SelectObject(__this->hDC,__this->hFont);
      }    
      return TRUE;
    }
    */
    
    if(HIWORD(wParam) == BN_CLICKED)
    {
      if(LOWORD(wParam) == IDOK)
      {
        OPTDLG_close(__this) ;
        return TRUE;
      }
      else if(LOWORD(wParam) == IDCANCEL)
      {
        OPTDLG_close(__this) ;
        return TRUE ;
      }
    }
    break ;
  case WM_GETMINMAXINFO:{
      //if(__this->cx == 0 || __this->cy == 0){
      //    break;
      //}
      int max_height = MAX_FONTHEIGHT + __this->height_offset + YSTART*2;
      int min_height = MIN_FONTHEIGHT + __this->height_offset + YSTART*2;
      int cx, cy, width, height;
      GetWidthHeight(__this, &cx, &cy, &width, &height);

      MINMAXINFO *info = (MINMAXINFO*)lParam;
      info->ptMaxSize.x = width;
      info->ptMaxSize.y = max_height;
      info->ptMinTrackSize.x = width;
      info->ptMinTrackSize.y = min_height;
      info->ptMaxTrackSize.x = width;
      info->ptMaxTrackSize.y = max_height;
      break;
  }
  case WM_SIZING:{
      GetClientRect(hDlg,&crect);
      int cx, cy, width, height;
      RECT *rc = (RECT*)lParam;
      create_font(__this, crect.bottom-YSTART*2, 0, __this->font_name);
      GetWidthHeight(__this, &cx, &cy, &width, &height);
      CheckSize(wParam, lParam, cx, cy, __this->width_offset, __this->height_offset);
      return TRUE;
  }
/*  case WM_SIZE:
    if(wParam == SIZE_RESTORED)
    {
      GetClientRect(hDlg,&crect);
      create_font(__this, crect.bottom-8, 0, __this->font_name);
    }
    break;
*/  
  default:
    break; 
	}

	return FALSE;
}

void OPTDLG_close(OPTDLG *__this)
{
  if(__this->dialog)
  {
    //追加 by Kobarin
    extern LONG GetTopLeftFromHwnd(HWND hWnd);
    __this->topleft = GetTopLeftFromHwnd(__this->dialog);
    RECT rect;
    GetClientRect(__this->dialog, &rect);
    __this->font_height = rect.bottom - YSTART*2;
    //ここまで
    KillTimer(__this->dialog, 1);
    RemoveProp(__this->dialog, L"INFO") ;
    DestroyWindow(__this->dialog) ;
    __this->dialog = NULL ;
 
    DeleteObject((HGDIOBJ)__this->hFont);
    DeleteObject((HGDIOBJ)__this->hBitmap);
    DeleteDC(__this->hDC);
  }
}

static void setDefaultGuiFont(HWND hWnd)
{
  SendMessage(hWnd, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), MAKELPARAM(FALSE, 0));    
}

void OPTDLG_open(OPTDLG *__this, HWND hParent, HINSTANCE hInst, int left, int top, const wchar_t *font_name, int font_height)
{
  if(!__this->dialog)
  {
    __this->dialog = CreateDialog(hInst, MAKEINTRESOURCE(IDD_OPTDLG), hParent, dlgProc ) ;
    assert(__this->dialog) ;
    RECT rc1;
    RECT rc2;
    GetWindowRect(__this->dialog, &rc1);
    GetClientRect(__this->dialog, &rc2);
    __this->width_offset = (rc1.right - rc1.left) - (rc2.right - rc2.left);
    __this->height_offset = (rc1.bottom - rc1.top) - (rc2.bottom - rc2.top);

    SetProp(__this->dialog, L"OPT", __this) ;
    SetTimer(__this->dialog, 1, FRAME_PERIOD, NULL) ;
    __this->hDC = CreateCompatibleDC(GetDC(__this->dialog));
    wcsncpy_s(__this->font_name, font_name, _TRUNCATE);
    create_font(__this, font_height, 0, font_name);
    int cx, cy, width, height;
    GetWidthHeight(__this, &cx, &cy, &width, &height);
    SetWindowPos(__this->dialog,HWND_TOPMOST,left,top, width, height, 0);
  }
  ShowWindow(__this->dialog, SW_SHOW) ;
}

OPTDLG *OPTDLG_new(void)
{
  OPTDLG *ret ;

  if(!(ret = (OPTDLG *)malloc(sizeof(OPTDLG)))) return NULL ;
  memset(ret, 0, sizeof(OPTDLG));//added by Kobarin(文字化け修正)
  ret->dialog = NULL ;
  ret->delay_in_ms = 0;
  ret->lyrics_pos = 0;
  ret->hFont = NULL;
  ret->hBitmap = NULL;
  ret->hDC = NULL;
  return ret;
}

void OPTDLG_delete(OPTDLG *__this)
{
  OPTDLG_close(__this) ;
  free(__this) ;
}