/** 
 *  Yudit Unicode Editor Source File
 *
 *  GNU Copyright (C) 1997-2023  Gaspar Sinai <gaspar@yudit.org>  
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License, version 2,
 *  dated June 1991. See file COPYYING for details.
 *
 *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "gui/SKMapPanel.h"
#include "swidget/SIconFactory.h"

#include "stoolkit/STypes.h"
#include "stoolkit/SUtil.h"
#include "stoolkit/SEncoder.h"

SKMapPanel::SKMapPanel (void)
{
  SPanel::forceLayout (SLayout (SDimension (2000, 3000)));
  selectedKMap = -1;
  titleLabel = new SLabel (translate ("Current KMap:"));
  add (titleLabel);
  currentLabel = new SLabel ("");
  sampleTitleLabel = new SLabel (translate ("Characters:"));
  sampleLabel = new SLabel ("");
  listBox = new SListBox(translate ("Available KMaps"));
  listBox->setListListener(this);
  replaceButton = new SButton ("", SIconFactory::getIcon("Right"));
  replaceButton->setButtonListener(this);
  SStringVector v;
  v.append (translate ("F-Key"));
  v.append (translate ("Current KMaps"));
  kmapTable = new STextTable (v);
  kmapTable->setListListener(this);
  SStringVector s;
  s.append (translate ("Key Input"));
  s.append (translate ("Output"));
  sample = new STextTable (s);
  sample->setListListener(this);
  
  add (currentLabel);
  add (sampleLabel);
  add (sampleTitleLabel);
  add (listBox);
  add (replaceButton);
  add (kmapTable);
  add (sample);
  SStringVector bext = SEncoder::external();
  
  SBinHashtable<bool> ph;
  unsigned int ib;
  unsigned int ic;
  for (ib=0; ib<bext.size(); ib++)
  {
    /* this is an input method and it is misleading. */
    if (bext[ib].match("jis*"))
    {
      continue;
    }
    else if (bext[ib].match("gb-*"))
    {
      continue;
    }
    else if (bext[ib].match("ksc-*"))
    {
      continue;
    }
    else if (bext[ib].match("ksx-*"))
    {
      continue;
    }
    else if (bext[ib].match("cp-*"))
    {
      continue;
    }
    else if (bext[ib].match("koi8-*"))
    {
      continue;
    }
    else if (bext[ib].match("IS_*")) /* ISCII maps */
    {
      continue;
    }
    else if (bext[ib].match("iso-*"))
    {
      continue;
    }
    else if (bext[ib] == "precompose")
    {
      continue;
    }
    else if (bext[ib] == "precompose1")
    {
      continue;
    }
    // This was only available in yudit-3.0.8 betas.
    // later it was renamed to precompose1 to indicate
    // that it deals with 1 character compositions.
    else if (bext[ib] == "precompose2")
    {
      continue;
    }
    else if (bext[ib] == "unicode")
    {
      continue;
    }
    else if (bext[ib] == "category")
    {
      continue;
    }
    else if (bext[ib] == "cluster")
    {
      continue;
    }
    else if (bext[ib] == "indic")
    {
      continue;
    }
    else if (bext[ib] == "wingreek")
    {
      continue;
    }
    ph.put (bext[ib], true);
  }
  ph.remove ("kinput2");
  ph.remove ("WindowsIM");
  ph.remove ("straight");
  ph.remove ("freehand");
  ph.remove ("None");
  ph.remove ("none");
  ph.remove ("unicode");
  ph.remove ("Unicode");
  /* these are converter maps, unlikely to work as keymaps */
  ph.remove ("mirroring");
  ph.remove ("nastaliq");
  ph.remove ("rovas");
  ph.remove ("shape");
  ph.remove ("tcvn");
  ph.remove ("viscii");
  ph.remove ("windows-1255");
  ph.remove ("tscii");
  ph.remove ("mik");

  SStringVector addEnc;
  for (ic=0; ic<ph.size(); ic++)
  {
    for (ib=0; ib<ph.size(ic); ib++)
    {
       bool vle = ph.get (ic, ib);
       if (vle)
       {
         addEnc.append (ph.key (ic, ib));
       }
    }
  }
  addEnc.sort ();
#ifdef USE_WINAPI
  addEnc.insert (0, "WindowsIM");
#else
  addEnc.insert (0, "kinput2");
#endif
  addEnc.insert (0, "unicode");
  addEnc.insert (0, "freehand");
  addEnc.insert (0, "straight");
  addEnc.insert (0, "None");
  allinputs = addEnc;
  listBox->setText (allinputs);
  //recalc();
}

SKMapPanel::~SKMapPanel ()
{
}

void
SKMapPanel::setList (const SStringVector& _kmapList, int selected)
{
  if (_kmapList.size()==0) return;
  SStringVector key;
  selectedKMap = selected;
  kmapList = _kmapList;

  currentLabel->setText (kmapList[selected]);
  char s[64];
  unsigned int i;
  for (i=0; i<kmapList.size(); i++)
  {
    snprintf (s, 64, "F%u", i+1);
    key.append (s);
  }
  while (i<12)
  {
    snprintf (s, 64, "F%u", i+1);
    key.append (s);
    kmapList.append ("None");
    i++;
  }
  SStringTable t;
  t.append (key);
  t.append (kmapList);

  kmapTable->setText (t);
  kmapTable->selectItem (selected);
  getSample();
}

void
SKMapPanel::setFont (const SString& font, double fontSize)
{
  titleLabel->setFont (font, fontSize);
  currentLabel->setFont (font, fontSize);
  sampleLabel->setFont (font, fontSize);
  sampleTitleLabel->setFont (font, fontSize);
  listBox->setFont (font, fontSize);
  replaceButton->setFont (font, fontSize);
  kmapTable->setFont (font, fontSize);
  sample->setFont (font, fontSize);
  //recalc();
}

void
SKMapPanel::setFontSize (double fontSize)
{
  titleLabel->setFontSize (fontSize);
  currentLabel->setFontSize (fontSize);
  sampleLabel->setFontSize (fontSize);
  sampleTitleLabel->setFontSize (fontSize);
  listBox->setFontSize (fontSize);
  replaceButton->setFontSize (fontSize);
  kmapTable->setFontSize (fontSize);
  sample->setFontSize (fontSize);
  //recalc();
}

void
SKMapPanel::setBackground (const SColor& bg)
{
  SPanel::setBackground(bg);
}

void
SKMapPanel::setForeground (const SColor& lrfg, const SColor& rlfg)
{
  kmapTable->setForeground (lrfg, rlfg);
  sample->setForeground (lrfg, rlfg);
}

const SStringVector&
SKMapPanel::getKMapList() const
{
  return kmapList;
}
int
SKMapPanel::getSelectedKMap() const
{
  return selectedKMap;
}

void
SKMapPanel::buttonPressedAccel (void* source, const SAccelerator* acc)
{
  int ksel = selectedKMap;
  int lsel = listBox->textList->getLastSelected();
  if (ksel < 0 || lsel < 0)
  {
    return;
  }
  SString si = listBox->textList->getLastSelectedText();
  
  SStringVector l = kmapList;
  l.remove ((unsigned int) ksel);
  l.insert ((unsigned int) ksel, si);
  setList (l, ksel);
}

void
SKMapPanel::itemSelected (void* source, const SAccelerator* acc)
{
  if (source == kmapTable)
  {
    if (kmapTable->getLastSelected()>=0)
    {
       selectedKMap = kmapTable->getLastSelected();
       currentLabel->setText (kmapList[(unsigned int)selectedKMap]);
       getSample ();
    }
  }
  else if (source == sample)
  {
    if (sample->getLastSelected()>=0)
    {
       sampleLabel->setText (unicodeValueOf(sampleList[
                (unsigned int)sample->getLastSelected()]));
    }
  }
}

void
SKMapPanel::setTitleForeground (const SColor& fg)
{
  listBox->setForeground (fg, fg);
  titleLabel->setForeground (fg);
  sampleTitleLabel->setForeground (fg);
  listBox->setLabelForeground (fg);
  kmapTable->setLabelForeground (fg);
  sample->setLabelForeground (fg);
}

void
SKMapPanel::setSliderBackground (const SColor& bg)
{
  listBox->setSliderBackground (bg);
  kmapTable->setSliderBackground (bg);
  sample->setSliderBackground (bg);
}

void
SKMapPanel::setLabelForeground (const SColor& fg)
{
  replaceButton->setForeground (fg);
  currentLabel->setForeground (fg);
  sampleLabel->setForeground (fg);
}

/**
 * recalculate the layout of components and set preferred size.
 * It calculates preferred size as well.
 */
void
SKMapPanel::recalc()
{
  SDimension tld = titleLabel->getPreferredSize();
  SDimension lbd = listBox->getPreferredSize();
  SDimension bd = replaceButton->getPreferredSize();
  SDimension kd = kmapTable->getPreferredSize();
  SDimension sd = sample->getPreferredSize();
  unsigned int w = lbd.width + 2 + bd.width + 2 + kd.width + 2 + sd.width;
  unsigned int h = tld.height + 2 + lbd.height;
  if ((w*2)/4 + 10> h) h = (w*2)/4+ 10;

  preferredSize = SDimension (w, h);
  SDimension d = preferredSize;

  titleLabel->setLayout (
    SLayout (
       SLocation (0, 0),
       SLocation (tld.width, tld.height),
       SLocation (0, 0),
       SLocation (100, 0)
    )
  );
  currentLabel->setLayout (
    SLayout (
       SLocation (tld.width+2, 0),
       SLocation (d.width/2 - 2, tld.height),
       SLocation (0, 0),
       SLocation (50, 0)
    )
  );
  unsigned int tw = sampleTitleLabel->getPreferredSize().width;
  sampleTitleLabel->setLayout (
    SLayout (
       SLocation (d.width/2, 0),
       SLocation (d.width/2 + tw, tld.height),
       SLocation (50, 0),
       SLocation (50, 0)
    )
  );
  sampleLabel->setLayout (
    SLayout (
       SLocation (d.width/2 + tw + 2, 0),
       SLocation (d.width, tld.height),
       SLocation (50, 0),
       SLocation (100, 0)
    )
  );
  unsigned int cx = (lbd.width * d.width) / w;
  listBox->setLayout (
    SLayout (
       SLocation (0, tld.height+5),
       SLocation (cx, d.height-10),
       SLocation (0, 0),
       SLocation (20, 100)
    )
  );
  cx = cx + 2;
  replaceButton->setLayout (
    SLayout (
       SLocation (cx, d.height/2-10),
       SLocation (cx + bd.width, d.height/2 + bd.height-10),
       SLocation (20, 50),
       SLocation (20, 50)
    )
  );
  cx += bd.width + 2;
  kmapTable->setLayout (
    SLayout (
       SLocation (cx, tld.height+5),
       SLocation (cx + (kd.width * d.width)/w , d.height-10),
       SLocation (20, 0),
       SLocation (40, 100)
    )
  );
  cx += (kd.width * d.width)/w + 2;
  sample->setLayout (
    SLayout (
       SLocation (cx, tld.height+5),
       SLocation (cx + (sd.width * d.width)/w , d.height-10),
       SLocation (40, 0),
       SLocation (100, 100)
    )
  );
  /* save current */
  SLayout goodlayout = layout;

  /* pretend we have this layout */
  SPanel::forceLayout (preferredSize);

  /* accept old layout */
  SPanel::setLayout (goodlayout);
  //resize (preferredSize);
}

void
SKMapPanel::getSample()
{
  if (selectedKMap < 0) return;
  SString map = kmapList[selectedKMap];
  SStringTable t;
  SStringVector kv;
  SStringVector vv;
  
  bool more = true;
  if (map == "None" || map == "none")
  {
     /* nothing */
  }
  else if (map.size()>1 && (map[0] == 'x' || map[0] == 'X') && (map[1] == '-'))
  {
     /* nothing */
  }
  else if (map == "straight" || map == "x-none" ||  map == "x-utf-8" || map == "x-ascii")
  {
     char s[64];
     for (unsigned int i=0; i<100 && i<128-32; i++)
     {
       s[0] = (char)(i+32);
       s[1] = 0;
       kv.append (s);
       vv.append (s);
    }
  }
  else
  {
    SStringVector allkv;
    SStringVector allvv;

    SEncoder enc (map);
    if (enc.isOK())
    {
      more = (enc.getDecoderMap (&allkv, &allvv, 2000) > 2000);
    }
    unsigned int i;
    // put . at the end.
    for (i=0; i<allkv.size(); i++) {
        SString key = allkv[i];
        if (key.size() > 0 && key[0] == '.') continue;
        kv.append (key);
        vv.append (allvv[i]);
    }
    for (i=0; i<allkv.size(); i++) {
        SString key = allkv[i];
        if (key.size() > 0 && key[0] != '.') continue;
        kv.append (key);
        vv.append (allvv[i]);
    }
  }
  if (more) 
  {
    kv.append ("..");
    vv.append ("..");
  }
  t.append (kv);
  t.append (vv);
  sampleList = vv;
  sample->setText (t);
  sampleLabel->setText ("");
}

/**
 * Set the xinputs in the form of: x-name-locale
 */
void
SKMapPanel::setXInputs (const SStringVector& list)
{
  SBinHashtable<int> check;
  /* no support yet */
  if (isWinAPI ()) return;

  //xinputs = SStringVector("x-utf-8,x-ascii");
  xinputs.clear();
  for (unsigned int i=0; i<list.size(); i++)
  {
    SStringVector v (list[i], ":");
    SString s = v[0];
    if (s=="x-utf-8")
    {
      s = list[i];
    }
    if (check.get (s) == 0)
    {
      check.put (s, 1);
      xinputs.append (list[i]);
    }
  }
  SStringVector v = allinputs;
  v.append (xinputs);
  listBox->setText (v);
}
