
/*

  KLayout Layout Viewer
  Copyright (C) 2006-2016 Matthias Koefferlein

  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 St, Fifth Floor, Boston, MA  02110-1301  USA

*/


#include "edtPCellParametersPage.h"
#include "layWidgets.h"

#include <QtGui/QFrame>
#include <QtGui/QCheckBox>
#include <QtGui/QLabel>
#include <QtGui/QLineEdit>
#include <QtGui/QComboBox>
#include <QtGui/QGridLayout>
#include <QtGui/QHBoxLayout>
#include <QtGui/QGroupBox>

namespace edt
{

static void set_value (const db::PCellParameterDeclaration &p, const db::Layout *layout, QWidget *widget, const tl::Variant &value)
{
  if (p.get_choices ().empty ()) {

    switch (p.get_type ()) {
      
    case db::PCellParameterDeclaration::t_int:
      {
        QLineEdit *le = dynamic_cast<QLineEdit *> (widget);
        if (le) {
          le->setText (value.cast<int> ().to_qstring ());
        }
      }
      break;

    case db::PCellParameterDeclaration::t_double:
      {
        QLineEdit *le = dynamic_cast<QLineEdit *> (widget);
        if (le) {
          le->setText (value.cast<double> ().to_qstring ());
        }
      }
      break;

    case db::PCellParameterDeclaration::t_string:
      {
        QLineEdit *le = dynamic_cast<QLineEdit *> (widget);
        if (le) {
          le->setText (value.to_qstring ());
        }
      }
      break;

    case db::PCellParameterDeclaration::t_list:
      {
        QLineEdit *le = dynamic_cast<QLineEdit *> (widget);
        if (le) {
          le->setText (value.to_qstring ());
        }
      }
      break;

    case db::PCellParameterDeclaration::t_layer:
      {
        lay::LayerSelectionComboBox *ly = dynamic_cast<lay::LayerSelectionComboBox *> (widget);
        if (ly) {

          db::LayerProperties lp;
          if (value.is_user<db::LayerProperties> ()) {
            lp = value.to_user<db::LayerProperties> ();
          } else {
            std::string s = value.to_string ();
            tl::Extractor ex (s.c_str ());
            lp.read (ex);
          }

          ly->set_current_layer (lp);

        }
      }
      break;

    case db::PCellParameterDeclaration::t_boolean:
      {
        QCheckBox *cbx = dynamic_cast<QCheckBox *> (widget);
        if (cbx) {
          cbx->setChecked (value.to_bool ());
        }
      }
      break;

    default:
      break;
    }

  } else {

    QComboBox *cb = dynamic_cast<QComboBox *> (widget);
    if (cb) {
      int i = 0;
      for (std::vector<tl::Variant>::const_iterator c = p.get_choices ().begin (); c != p.get_choices ().end (); ++c, ++i) {
        if (*c == value) {
          cb->setCurrentIndex (i);
        }
      }
    }

  }
}

PCellParametersPage::PCellParametersPage (QWidget *parent, const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type &parameters)
  : QScrollArea (parent), mp_pcell_decl (pcell_decl), mp_layout (layout), mp_view (view), m_cv_index (cv_index)
{
  m_parameters = parameters;

  QFrame *fi = new QFrame (this);
  QWidget *inner_frame = fi;
  fi->setFrameShape (QFrame::NoFrame);
  setFrameShape (QFrame::NoFrame);

  QGridLayout *inner_grid = new QGridLayout (inner_frame);
  inner_frame->setLayout (inner_grid);

  QWidget *main_frame = inner_frame;
  QGridLayout *main_grid = inner_grid;

  int main_row = 0;
  int row = 0;
  std::string group_title;

  int r = 0;
  for (std::vector<db::PCellParameterDeclaration>::const_iterator p = pcell_decl->parameter_declarations ().begin (); p != pcell_decl->parameter_declarations ().end (); ++p, ++r) {

    if (p->is_hidden () || p->get_type () == db::PCellParameterDeclaration::t_shape) {
      m_widgets.push_back (0);
      continue;
    }

    std::string gt, description;
    size_t tab = p->get_description ().find ("\t");
    if (tab != std::string::npos) {
      gt = std::string (p->get_description (), 0, tab);
      description = std::string (p->get_description (), tab + 1, std::string::npos);
    } else {
      description = p->get_description ();
    }

    if (gt != group_title) {

      if (! gt.empty ()) {

        //  create a new group
        QGroupBox *gb = new QGroupBox (main_frame);
        gb->setTitle (tl::to_qstring (gt));
        main_grid->addWidget (gb, main_row, 0, 1, 2);
        
        inner_grid = new QGridLayout (gb);
        gb->setLayout (inner_grid);
        inner_frame = gb;

        row = 0;
        ++main_row;

      } else {

        //  back to the main group
        inner_grid = main_grid;
        inner_frame = main_frame;
        row = main_row;

      }

      group_title = gt;

    } 

    inner_grid->addWidget (new QLabel (tl::to_qstring (description), inner_frame), row, 0);

    tl::Variant value;
    if (r < int (parameters.size ())) {
      value = parameters [r];
    } else {
      value = p->get_default ();
    }

    if (p->get_choices ().empty ()) {

      switch (p->get_type ()) {
        
      case db::PCellParameterDeclaration::t_int:
      case db::PCellParameterDeclaration::t_double:
        {
          QFrame *f = new QFrame (inner_frame);
          QHBoxLayout *hb = new QHBoxLayout (f);
          hb->setMargin (0);
          f->setLayout (hb);

          QLineEdit *le = new QLineEdit (f);
          le->setEnabled (! p->is_readonly ());
          hb->addWidget (le);
          le->setMaximumWidth (150);
          m_widgets.push_back (le);

          QLabel *ul = new QLabel (f);
          hb->addWidget (ul, 1);
          ul->setText (tl::to_qstring (p->get_unit ()));

          inner_grid->addWidget (f, row, 1);
        }
        break;

      case db::PCellParameterDeclaration::t_string:
      case db::PCellParameterDeclaration::t_shape:
      case db::PCellParameterDeclaration::t_list:
        {
          QLineEdit *le = new QLineEdit (inner_frame);
          le->setEnabled (! p->is_readonly ());
          m_widgets.push_back (le);
          inner_grid->addWidget (le, row, 1);
        }
        break;

      case db::PCellParameterDeclaration::t_layer:
        {
          lay::LayerSelectionComboBox *ly = new lay::LayerSelectionComboBox (inner_frame);
          ly->setEnabled (! p->is_readonly ());
          ly->set_no_layer_available (true);
          ly->set_view (mp_view, m_cv_index, true /*all layers*/);
          m_widgets.push_back (ly);
          inner_grid->addWidget (ly, row, 1);
        }
        break;

      case db::PCellParameterDeclaration::t_boolean:
        {
          QCheckBox *cbx = new QCheckBox (inner_frame);
          cbx->setEnabled (! p->is_readonly ());
          m_widgets.push_back (cbx);
          inner_grid->addWidget (cbx, row, 1);
        }
        break;

      default:
        m_widgets.push_back (0);
        break;
      }

    } else {

      QComboBox *cb = new QComboBox (inner_frame);

      int i = 0;
      for (std::vector<tl::Variant>::const_iterator c = p->get_choices ().begin (); c != p->get_choices ().end (); ++c, ++i) {
        if (i < int (p->get_choice_descriptions ().size ())) {
          cb->addItem (tl::to_qstring (p->get_choice_descriptions () [i]));
        } else {
          cb->addItem (tl::to_qstring (c->to_string ()));
        }
      }

      connect (cb, SIGNAL (activated (int)), this, SLOT (activated (int)));
      cb->setEnabled (! p->is_readonly ());
      cb->setMinimumContentsLength (30);
      cb->setSizeAdjustPolicy (QComboBox::AdjustToMinimumContentsLengthWithIcon);
      m_widgets.push_back (cb);
      inner_grid->addWidget (cb, row, 1);

    }

    set_value (*p, mp_layout, m_widgets.back (), value);

    ++row;
    if (inner_frame == main_frame) {
      ++main_row;
    }

  }

  setWidget (main_frame);
  main_frame->show ();

  //  does a first coerce and update
  get_parameters ();
}

void  
PCellParametersPage::activated (int)
{
  //  does a coerce and update
  get_parameters ();
}

void  
PCellParametersPage::clicked ()
{
  //  does a coerce and update
  get_parameters ();
}

std::vector<tl::Variant> 
PCellParametersPage::get_parameters () 
{
  std::vector<tl::Variant> parameters;

  int r = 0;
  for (std::vector<db::PCellParameterDeclaration>::const_iterator p = mp_pcell_decl->parameter_declarations ().begin (); p != mp_pcell_decl->parameter_declarations ().end (); ++p, ++r) {

    if (p->is_hidden () || p->get_type () == db::PCellParameterDeclaration::t_shape) {

      if (r < (int) m_parameters.size ()) {
        parameters.push_back (m_parameters [r]);
      } else {
        parameters.push_back (p->get_default ());
      }

    } else {

      parameters.push_back (tl::Variant ());

      if (p->get_choices ().empty ()) {

        switch (p->get_type ()) {
          
        case db::PCellParameterDeclaration::t_int:
          {
            QLineEdit *le = dynamic_cast<QLineEdit *> (m_widgets [r]);
            if (le) {
              int v = 0;
              tl::from_string (tl::to_string (le->text ()), v);
              parameters.back () = tl::Variant (v);
            }
          }
          break;

        case db::PCellParameterDeclaration::t_double:
          {
            QLineEdit *le = dynamic_cast<QLineEdit *> (m_widgets [r]);
            if (le) {
              double v = 0;
              tl::from_string (tl::to_string (le->text ()), v);
              parameters.back () = tl::Variant (v);
            }
          }
          break;

        case db::PCellParameterDeclaration::t_string:
          {
            QLineEdit *le = dynamic_cast<QLineEdit *> (m_widgets [r]);
            if (le) {
              parameters.back () = tl::Variant (tl::to_string (le->text ()));
            }
          }
          break;

        case db::PCellParameterDeclaration::t_list:
          {
            QLineEdit *le = dynamic_cast<QLineEdit *> (m_widgets [r]);
            if (le) {
              std::vector<std::string> values = tl::split (tl::to_string (le->text ()), ",");
              parameters.back () = tl::Variant (values.begin (), values.end ());
            }
          }
          break;

        case db::PCellParameterDeclaration::t_layer:
          {
            lay::LayerSelectionComboBox *ly = dynamic_cast<lay::LayerSelectionComboBox *> (m_widgets [r]);
            if (ly) {
              parameters.back () = tl::Variant (ly->current_layer_props ());
            }
          }
          break;
        case db::PCellParameterDeclaration::t_boolean:
          {
            QCheckBox *cbx = dynamic_cast<QCheckBox *> (m_widgets [r]);
            if (cbx) {
              parameters.back () = tl::Variant (cbx->isChecked ());
            }
          }
          break;

        default:
          break;
        }

      } else {

        QComboBox *cb = dynamic_cast<QComboBox*> (m_widgets [r]);
        if (cb && cb->currentIndex () >= 0 && cb->currentIndex () < int (p->get_choices ().size ())) {
          parameters.back () = p->get_choices () [cb->currentIndex ()];
        }

      }

    }

  }

  //  call coerce on the parameters
  mp_pcell_decl->coerce_parameters (*mp_layout, parameters);

  set_parameters (parameters);

  return parameters;
}

void 
PCellParametersPage::set_parameters (const  std::vector<tl::Variant> &parameters) 
{
  //  write the changed value back
  size_t r = 0;
  for (std::vector<db::PCellParameterDeclaration>::const_iterator p = mp_pcell_decl->parameter_declarations ().begin (); p != mp_pcell_decl->parameter_declarations ().end (); ++p, ++r) {
    if (r < parameters.size () && m_widgets [r]) {
      set_value (*p, mp_layout, m_widgets [r], parameters [r]);
    }
  }
}

}


