// *********************************************************************
//    Copyright (c) 1989-2002  Warren Furlow
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// *********************************************************************

// *********************************************************************
// HP41Graphics.cpp: implementation file
// *********************************************************************

#include"StdAfx.h"
#include"V41.h"

/****************************/
// used to map ASCII to LCD display chars - this is LCD4.ttf mappings,
// if the value here is less than 0x80 then add 0x20 to get LCD.fon mapping
/****************************/
const byte HP41::ASCIItoLCD[128]=
    {
    0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,
    0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,0x3a,
    0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0xa3,0x2d,0xa2,0x2f,    // exceptions ,.
    0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0xa1,0x3a,0x3c,0x3d,0x3e,0x3f,    // exceptions :;
    0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
    0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
    0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
    0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x3a,0x7d,0x3a,0x3a,    // exceptions |~
    };

/****************************/
void HP41::ConvertASCIItoLCD(
  const char *szASCII,
  CString& str_dis) const
  {
  char dis_chr;

  for (uint i=0;i<strlen(szASCII);i++)
    {
    if ((byte)(szASCII[i])<0x80)
      {
      dis_chr=ASCIItoLCD[szASCII[i]];
      }
    else
      {
      dis_chr=szASCII[i];
      }
    if (eFont!=eFontLCD4 && (byte)(dis_chr)<0x80)
      dis_chr+=0x20;

    if (eFont==eFontLCD4 && dis_chr==0) // 0 fix for TTF font char '@' on Win 7 and later
      dis_chr=-128;

    str_dis+=dis_chr;
    }
  }

/****************************/
int HP41::GetContrast() const
  {
  return(Contrast);
  }

/****************************/
void HP41::SetContrast(int ContrastVal)
  {
  Contrast=ContrastVal&0xf;

  // force screen refresh
  theApp.m_pMainWnd->InvalidateRect(pRectLCD,FALSE);
  theApp.m_pMainWnd->InvalidateRect(pRectAnnun,FALSE);
  }

/****************************/
int HP41::GetFontColor() const
  {
  return(FontColor);
  }

/****************************/
void HP41::SetFontColor(int ColorVal)
  {
  FontColor=ColorVal;

  // force screen refresh
  theApp.m_pMainWnd->InvalidateRect(pRectLCD,FALSE);
  theApp.m_pMainWnd->InvalidateRect(pRectAnnun,FALSE);
  }


/****************************/
LOGPALETTE *HP41::GetPalKeyboard() const
  {
  return(pPalKeyboard);
  }


/****************************/
COLORREF HP41::GetLcdColor(
  word Contrast,                   // 0-15 value for contrast (half nut)
  COLORREF FontColor)              // RBG font color value
  {
  if (FontColor==0)             // black
    {
    int Gray;
    if (Contrast>=10)
      Gray=0;                   // solid black
    else
      Gray=9-Contrast;          // results range from 9 to 0 - larger numbers are lighter gray
    FontColor=Gray<<20|Gray<<12|Gray<<4;
    }
  return FontColor;
  }


/****************************/
void HP41::Draw(CDC *pCDC)
  {
  CBitmap *CBMOld;
  CDC CDCMem;
  CPalette *pCPalOld;

  if (!pBMKeyboard)
    return;

  fUsePal=(GetDeviceCaps(pCDC->m_hAttribDC,BITSPIXEL)==8)&&(pBMKeyboard->biClrUsed==256);
  if (fUsePal)
    {
    if (!CPalKeyboard.m_hObject)        // create it if not already created
      CPalKeyboard.CreatePalette(theApp.pPalForeground);
    pCPalOld=pCDC->SelectPalette(&CPalKeyboard,FALSE);
    pCDC->RealizePalette();
    }
  if (!CBMKeyboard.m_hObject)           // keep keyboard bitmap in memory for redrawing keys
    {
    CBMKeyboard.CreateCompatibleBitmap(pCDC,pBMKeyboard->biWidth,pBMKeyboard->biHeight);
    SetDIBits(pCDC->m_hDC,(HBITMAP)CBMKeyboard.m_hObject,0,pBMKeyboard->biHeight,
      (char*)pBMKeyboard+pBMKeyboard->biSize+sizeof(RGBQUAD)*pBMKeyboard->biClrUsed,
      fUsePal?(BITMAPINFO*)pPalIndexKeyboard:(BITMAPINFO*)pBMKeyboard,
      fUsePal?DIB_PAL_COLORS:DIB_RGB_COLORS);
    }

  // make a copy to draw on
  CBitmap CBMTemp;
  CBMTemp.CreateCompatibleBitmap(pCDC,pBMKeyboard->biWidth,pBMKeyboard->biHeight);
  SetDIBits(pCDC->m_hDC,(HBITMAP)CBMTemp.m_hObject,0,pBMKeyboard->biHeight,
    (char*)pBMKeyboard+pBMKeyboard->biSize+sizeof(RGBQUAD)*pBMKeyboard->biClrUsed,
    fUsePal?(BITMAPINFO*)pPalIndexKeyboard:(BITMAPINFO*)pBMKeyboard,
    fUsePal?DIB_PAL_COLORS:DIB_RGB_COLORS);

  CDCMem.CreateCompatibleDC(pCDC);
  CBMOld=CDCMem.SelectObject(&CBMTemp);

  if (fUsePal)                     // not sure if this is really necessary
    CDCMem.SelectPalette(pCPalOld,FALSE);

  if (DisplayOn)
    {
    CDCMem.SetBkMode(TRANSPARENT);
    CDCMem.SetTextColor(GetLcdColor(Contrast,FontColor));

    // output the display string and annunciator
    char szDisplay[56];
    GetLCD(szDisplay);
    CDCMem.SelectObject(CFontLCD);
    CDCMem.TextOut(pRectLCD->left,pRectLCD->top,szDisplay,24);
    if (eKeyboard==eKeyboardTiny)
      {
      GetShortAnnunciator(szDisplay);
      CDCMem.SelectObject(CFontAnnun);
      CDCMem.TextOut(pRectAnnun->left,pRectAnnun->top,szDisplay,30);
      }
    else if (eKeyboard==eKeyboardXLarge)
      {
      GetXAnnunciator(szDisplay);
      CDCMem.SelectObject(CFontAnnun);
      CDCMem.TextOut(pRectAnnun->left,pRectAnnun->top,szDisplay,51);
      }
    else
      {
      GetAnnunciator(szDisplay);
      CDCMem.SelectObject(CFontAnnun);
      CDCMem.TextOut(pRectAnnun->left,pRectAnnun->top,szDisplay,37);
      }
    }

  // draw activity indicator
  if (Indicator)
    {
    CBrush* pCBrushOld=CDCMem.SelectObject(IsSleeping()?&brushGray:&brushRed);
    CDCMem.Ellipse(&RectIndicator);
    CDCMem.SelectObject(pCBrushOld);
    }

  // close button upper right corner
  if (pRectCloseButton)
    {
    CPen penBlack(PS_SOLID,2,RGB(0,0,0));
    CPen* pCPenOld=CDCMem.SelectObject(&penBlack);
    CRect rectClose(pRectCloseButton);
    rectClose.InflateRect(-2,-2);
    CDCMem.MoveTo(rectClose.left,rectClose.top);
    CDCMem.LineTo(rectClose.right,rectClose.bottom);
    CDCMem.MoveTo(rectClose.left,rectClose.bottom);
    CDCMem.LineTo(rectClose.right,rectClose.top);
    CDCMem.SelectObject(pCPenOld);
    }

  pCDC->BitBlt(0,0,pBMKeyboard->biWidth,pBMKeyboard->biHeight,&CDCMem,0,0,SRCCOPY);
  CDCMem.SelectObject(CBMOld);
  CDCMem.DeleteDC();

  UpdateDisplay=0;
  UpdateAnnun=0;
  }


/***************************/
// Get LCD string from registers based on current font mapping.
// Always 24 chars long, Could have 0 as a char value
/***************************/
void HP41::GetLCD(char *str) const
  {
  char *ptr;
  char ch;
  char punct;

  ptr=str;
  for (int i=0;i<12;i++)
    {
    // chr= cbbaaaa = 0x00 - 0x7f
    ch=(DIS_C_REG[11-i]<<6) | ((DIS_B_REG[11-i]&0x03)<<4) | DIS_A_REG[11-i];
    punct=(DIS_B_REG[11-i]&0x0c)>>2;
    if (eFont==eFontLCD4)
      {
      if (ch==0) ch=-128; // 0 fix for TTF font char '@' on Win 7 and later
      *(ptr++)=ch;
      }
    else
      *(ptr++)=ch+0x20;
    switch (punct)
      {
      case 0:     // none
        *ptr=(char)160;
        break;
      case 1:     // period
        *ptr=(char)162;
        break;
      case 2:     // colon
        *ptr=(char)161;
        break;
      case 3:     // comma
        *ptr=(char)163;
        break;
      }
    if (UseAltPunc)     // slightly narrower punc chars
      *ptr+=4;
    ptr++;
    }
  }

/***************************/
// Gets display string from registers using ASCII font mapping
// Null terminated string result
/***************************/
void HP41::GetDisplay(char *str) const
  {
  char *ptr;
  unsigned short wch;
  char punct;

  ptr=str;
  for (int i=0;i<12;i++)
    {
    word lcdchar=(DIS_C_REG[11-i]<<6) | (DIS_B_REG[11-i]<<4) | DIS_A_REG[11-i];
    decode_lcdchar(lcdchar,ptr++,&wch,&punct);
    if (punct)
      *(ptr++)=punct;
    }
  *ptr=0;
  }


/***************************/
//                     pixels in:       annun.fnt annun2.fnt annun3.fnt
#define BLANK_A 40  // M,T,0-5                  6          7          8
#define BLANK_B 41  // A-Z except M,T,I         5          6          7
#define BLANK_C 42  // I                        4          5          5
#define BLANK_D 43  // seperators               3          6          4
#define BLANK_E 44  // between G RAD            1          1          1
/***************************/

/***************************/
// Builds full Annunicator string from registers.
// Always 51 chars long
/***************************/
void HP41::GetXAnnunciator(char *str) const
  {
  char *ptr=str;
  if (DIS_ANNUN_REG&0x0800)  // bat
    {
    *(ptr++)='B';
    *(ptr++)='A';
    *(ptr++)='T';
    }
  else
    {
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_A;
    }
  *(ptr++)=BLANK_D;
  *(ptr++)=BLANK_D;
  *(ptr++)=BLANK_D;

  if (DIS_ANNUN_REG&0x0400)  // user
    {
    *(ptr++)='U';
    *(ptr++)='S';
    *(ptr++)='E';
    *(ptr++)='R';
    }
  else
    {
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    }
  *(ptr++)=BLANK_D;
  *(ptr++)=BLANK_D;
  *(ptr++)=BLANK_D;

  if (DIS_ANNUN_REG&0x0200)  // g
    *(ptr++)='G';
  else
    *(ptr++)=BLANK_B;
  *(ptr++)=BLANK_E;
  if (DIS_ANNUN_REG&0x0100)  // rad
    {
    *(ptr++)='R';
    *(ptr++)='A';
    *(ptr++)='D';
    }
  else
    {
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    }
  *(ptr++)=BLANK_D;
  *(ptr++)=BLANK_D;
  *(ptr++)=BLANK_D;

  if (DIS_ANNUN_REG&0x0080)  // shift
    {
    *(ptr++)='S';
    *(ptr++)='H';
    *(ptr++)='I';
    *(ptr++)='F';
    *(ptr++)='T';
    }
  else
    {
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_C;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_A;
    }
  *(ptr++)=BLANK_D;
  *(ptr++)=BLANK_D;
  *(ptr++)=BLANK_D;
  *(ptr++)=BLANK_D;

  if (DIS_ANNUN_REG&0x0040)  // 0
    *(ptr++)='0';
  else
    *(ptr++)=BLANK_A;
  if (DIS_ANNUN_REG&0x0020)  // 1
    *(ptr++)='1';
  else
    *(ptr++)=BLANK_A;
  if (DIS_ANNUN_REG&0x0010)  // 2
    *(ptr++)='2';
  else
    *(ptr++)=BLANK_A;
  if (DIS_ANNUN_REG&0x0008)  // 3
    *(ptr++)='3';
  else
    *(ptr++)=BLANK_A;
  if (DIS_ANNUN_REG&0x0004)  // 4
    *(ptr++)='4';
  else
    *(ptr++)=BLANK_A;
  *(ptr++)=BLANK_D;
  *(ptr++)=BLANK_D;
  *(ptr++)=BLANK_D;
  *(ptr++)=BLANK_D;

  if (DIS_ANNUN_REG&0x0002)  // prgm
    {
    *(ptr++)='P';
    *(ptr++)='R';
    *(ptr++)='G';
    *(ptr++)='M';
    }
  else
    {
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_A;
    }
  *(ptr++)=BLANK_D;
  *(ptr++)=BLANK_D;
  *(ptr++)=BLANK_D;

  if (DIS_ANNUN_REG&0x0001)  // alpha
    {
    *(ptr++)='A';
    *(ptr++)='L';
    *(ptr++)='P';
    *(ptr++)='H';
    *(ptr++)='A';
    }
  else
    {
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    }
  }


/***************************/
// Builds full Annunicator string from registers.
// Always 37 chars long
/***************************/
void HP41::GetAnnunciator(char *str) const
  {
  char *ptr=str;
  if (DIS_ANNUN_REG&0x0800)  // bat
    {
    *(ptr++)='B';
    *(ptr++)='A';
    *(ptr++)='T';
    }
  else
    {
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_A;
    }
  *(ptr++)=BLANK_D;

  if (DIS_ANNUN_REG&0x0400)  // user
    {
    *(ptr++)='U';
    *(ptr++)='S';
    *(ptr++)='E';
    *(ptr++)='R';
    }
  else
    {
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    }
  *(ptr++)=BLANK_D;

  if (DIS_ANNUN_REG&0x0200)  // g
    *(ptr++)='G';
  else
    *(ptr++)=BLANK_B;
  *(ptr++)=BLANK_E;
  if (DIS_ANNUN_REG&0x0100)  // rad
    {
    *(ptr++)='R';
    *(ptr++)='A';
    *(ptr++)='D';
    }
  else
    {
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    }
  *(ptr++)=BLANK_D;

  if (DIS_ANNUN_REG&0x0080)  // shift
    {
    *(ptr++)='S';
    *(ptr++)='H';
    *(ptr++)='I';
    *(ptr++)='F';
    *(ptr++)='T';
    }
  else
    {
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_C;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_A;
    }
  *(ptr++)=BLANK_D;

  if (DIS_ANNUN_REG&0x0040)  // 0
    *(ptr++)='0';
  else
    *(ptr++)=BLANK_A;
  if (DIS_ANNUN_REG&0x0020)  // 1
    *(ptr++)='1';
  else
    *(ptr++)=BLANK_A;
  if (DIS_ANNUN_REG&0x0010)  // 2
    *(ptr++)='2';
  else
    *(ptr++)=BLANK_A;
  if (DIS_ANNUN_REG&0x0008)  // 3
    *(ptr++)='3';
  else
    *(ptr++)=BLANK_A;
  if (DIS_ANNUN_REG&0x0004)  // 4
    *(ptr++)='4';
  else
    *(ptr++)=BLANK_A;
  *(ptr++)=BLANK_D;

  if (DIS_ANNUN_REG&0x0002)  // prgm
    {
    *(ptr++)='P';
    *(ptr++)='R';
    *(ptr++)='G';
    *(ptr++)='M';
    }
  else
    {
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_A;
    }
  *(ptr++)=BLANK_D;

  if (DIS_ANNUN_REG&0x0001)  // alpha
    {
    *(ptr++)='A';
    *(ptr++)='L';
    *(ptr++)='P';
    *(ptr++)='H';
    *(ptr++)='A';
    }
  else
    {
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    }
  }


/***************************/
// Gets shortened Annunicator string from registers.
// Always 30 chars long
/***************************/
void HP41::GetShortAnnunciator(char *str) const
  {
  char *ptr=str;
  if (DIS_ANNUN_REG&0x0800)  // bat
    *(ptr++)='B';
  else
    *(ptr++)=BLANK_B;
  *(ptr++)=BLANK_D;

  if (DIS_ANNUN_REG&0x0400)  // user
    {
    *(ptr++)='U';
    *(ptr++)='S';
    *(ptr++)='R';
    }
  else
    {
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    }
  *(ptr++)=BLANK_D;

  if (DIS_ANNUN_REG&0x0200)  // g
    *(ptr++)='G';
  else
    *(ptr++)=BLANK_B;
  *(ptr++)=BLANK_E;
  if (DIS_ANNUN_REG&0x0100)  // rad
    {
    *(ptr++)='R';
    *(ptr++)='D';
    }
  else
    {
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    }
  *(ptr++)=BLANK_D;

  if (DIS_ANNUN_REG&0x0080)  // shift
    {
    *(ptr++)='S';
    *(ptr++)='H';
    *(ptr++)='F';
    *(ptr++)='T';
    }
  else
    {
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_A;
    }
  *(ptr++)=BLANK_D;

  if (DIS_ANNUN_REG&0x0040)  // 0
    *(ptr++)='0';
  else
    *(ptr++)=BLANK_A;
  if (DIS_ANNUN_REG&0x0020)  // 1
    *(ptr++)='1';
  else
    *(ptr++)=BLANK_A;
  if (DIS_ANNUN_REG&0x0010)  // 2
    *(ptr++)='2';
  else
    *(ptr++)=BLANK_A;
  if (DIS_ANNUN_REG&0x0008)  // 3
    *(ptr++)='3';
  else
    *(ptr++)=BLANK_A;
  if (DIS_ANNUN_REG&0x0004)  // 4
    *(ptr++)='4';
  else
    *(ptr++)=BLANK_A;
  *(ptr++)=BLANK_D;

  if (DIS_ANNUN_REG&0x0002)  // prgm
    {
    *(ptr++)='P';
    *(ptr++)='G';
    *(ptr++)='M';
    }
  else
    {
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_A;
    }
  *(ptr++)=BLANK_D;

  if (DIS_ANNUN_REG&0x0001)  // alpha
    {
    *(ptr++)='A';
    *(ptr++)='L';
    *(ptr++)='P';
    *(ptr++)='A';
    }
  else
    {
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    *(ptr++)=BLANK_B;
    }
  }
