#ifndef _MIR_WCUSTOMTABLEVIEW_H_
#define _MIR_WCUSTOMTABLEVIEW_H_
/*
 * Copyright (c) 2011,
 * Technical University Of Gdansk,
 * Faculty of Electronics, Telecommunications and Informatics,
 * Multimedia Systems Department
 * and/or its affiliates. All rights reserved.
 */
#include <Wt/WEnvironment>
#include <Wt/WContainerWidget>
#include <Wt/WCompositeWidget>
#include <Wt/WAbstractItemModel>
#include <Wt/WTableView>
#include <Wt/WAnchor>
#include <Wt/WPushButton>
#include <Wt/WCheckBox>
#include <Wt/WComboBox>
#include <Wt/WLink>
#include <Wt/Dbo/Dbo>
#include <Wt/Dbo/QueryModel>
#include <Wt/WEvent>
#include <Wt/WHBoxLayout>
#include <Wt/WSlider>
#include <Wt/WEvent>

#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/any.hpp>

#include <vector>

#include "utils/mir_trkeys.h"
#include "utils/mir_cssclasses.h"
#include "widgets/mir_wcustomtableviewpushbutton.h"
#include "mir_databasemodule.h"
#include "resources/mir_sp_paramselresource.h"

namespace SyNaT
{
  namespace MIR
  {
    using namespace Wt;
    using namespace std;
    namespace dbo = Wt::Dbo;

    template <class Result>
    class WCustomTableView;

    template <class Result>
    class WCustomTableViewImpl : public WContainerWidget
    {
      friend class WCustomTableViewPushButton<Result>;

      public:

        WCustomTableViewImpl(DatabaseModule *dm, dbo::Session *session, bool likeStatement = false, WCustomTableView<Result> *ctv = 0)
          : m_customTableView(ctv), m_checkable(false)
          , m_current_page(0), m_nav_bar_created(false), m_num_of_pages_in_nav_bar(1)
          , m_first_page_in_nav_bar(0), m_last_page_in_nav_bar(m_num_of_pages_in_nav_bar-1)
          , m_dm(dm), m_session(session), m_pref_num_of_pages_in_nav_bar(4)
        {
          WContainerWidget *options_bar = new WContainerWidget();
          this->addWidget(options_bar);
          options_bar->setClearSides(Left | Right);
//          WPushButton *wpb = new WPushButton(tr(MIR_WCTV_SAVE_SEL), options_bar);
//          wpb->setStyleClass(MIR_STRINGIFY(MIR_CSS_PAGE_NAV_BUTTON));
//          new WText("&nbsp;&nbsp;&nbsp;", XHTMLText, options_bar);
          WAnchor *ar = new WAnchor(WLink(new SP::ParamsSelResource(this->m_dm)), tr(MIR_WCTV_EXPORT_SEL_PARS), options_bar);
          ar->setTarget(TargetNewWindow);
          options_bar = new WContainerWidget();
          this->addWidget(options_bar);
          options_bar->setClearSides(Left | Right);
          //this->m_txt = new WText(tr(MIR_TABLE_EXP_SEARCH).arg(WString::Empty), options_bar);
          //this->m_txt->disable();
          this->m_sle = new WLineEdit(options_bar);
          this->m_sle->setTextSize(30);

          this->m_scb = new WComboBox(options_bar);

          //this->m_sle->disable();
          //if (likeStatement)
          //{
          //  this->m_wcb = new WCheckBox(tr(MIR_TABLE_EXP_LIKE), options_bar);
          //  this->m_wcb->disable();
          //}

          this->m_wpb = new WPushButton(tr(MIR_WCTV_APPLY_SEARCH), options_bar);
          this->m_show_only_selected = new WCheckBox(tr(MIR_WCTV_SHOW_ONLY_SELECTED), options_bar);

          this->m_txt_selected = new WText(tr(MIR_WCTV_SELECTED), options_bar);
          this->m_txt_all_displayed = new WText(tr(MIR_WCTV_ALL_DISPLAYED), options_bar);

          options_bar = new WContainerWidget();
          this->addWidget(options_bar);
          options_bar->setClearSides(Left | Right);
          WHBoxLayout *layout = new WHBoxLayout();
          this->m_selectAll = new WPushButton(tr(MIR_WCTV_SELECT_ALL_FILTERED));
          layout->addWidget(this->m_selectAll);
          this->m_selectAll->setStyleClass(MIR_STRINGIFY(MIR_CSS_PAGE_NAV_BUTTON));
          this->m_inverseSelection = new WPushButton(tr(MIR_WCTV_INVERSE_SELECTION));
          layout->addWidget(this->m_inverseSelection);
          this->m_inverseSelection->setStyleClass(MIR_STRINGIFY(MIR_CSS_PAGE_NAV_BUTTON));
//          new WText("&nbsp;&nbsp;&nbsp;", XHTMLText, options_bar);
          this->m_unselectAll = new WPushButton(tr(MIR_WCTV_UNSELECT_ALL_FILTERED));
          layout->addWidget(this->m_unselectAll);
          this->m_unselectAll->setStyleClass(MIR_STRINGIFY(MIR_CSS_PAGE_NAV_BUTTON));
          options_bar->setLayout(layout, AlignTop | AlignJustify);
          options_bar = new WContainerWidget();
          options_bar->setClearSides(Left | Right);
          layout = new WHBoxLayout();
          this->m_wtv = new WTableView();
          layout->addWidget(this->m_wtv);
          this->m_slider = new WSlider(Vertical);
          this->m_slider->setTickPosition(WSlider::TicksRight);
          layout->addWidget(this->m_slider);
          options_bar->setLayout(layout, AlignTop | AlignJustify);
          this->addWidget(options_bar);
          this->m_page_nav_bar = new WContainerWidget();
          this->addWidget(this->m_page_nav_bar);
          this->m_page_nav_bar->setClearSides(Left | Right);
        }

        virtual ~WCustomTableViewImpl()
        {
        }

        WText &selectedText()
        {
          return *(this->m_txt_selected);
        }

        WText &displayedAllText()
        {
          return *(this->m_txt_all_displayed);
        }

        WLineEdit &searchLineEdit()
        {
          return *(this->m_sle);
        }

        WComboBox &searchComboBox()
        {
          return *(this->m_scb);
        }

        WPushButton &searchPushButton()
        {
          return *(this->m_wpb);
        }

        WCheckBox &showOnlySelectedCheckBox()
        {
          return *(this->m_show_only_selected);
        }

        //WCheckBox &searchCheckBoxLike()
        //{
        //  return *(this->m_wcb);
        //}

        WPushButton &selectAllPushButton()
        {
          return *(this->m_selectAll);
        }

        WPushButton &inverseSelection()
        {
          return *(this->m_inverseSelection);
        }

        WPushButton &unselectAllPushButton()
        {
          return *(this->m_unselectAll);
        }

        WSlider &slider()
        {
          return *(this->m_slider);
        }

        int update_slider()
        {
          int max = this->pageCount() + (this->pageCountReminder() > 0 ? -1 : -2);
          if (max < 0) max = 1;
          this->m_slider->setRange(0, max);
          int tickInterval = (int) (this->height().value() / max);

          if (tickInterval < 10)
            tickInterval = (int) ((10 * max) / this->height().value());

          if (tickInterval < 1)
            tickInterval = max;
          this->m_slider->setTickInterval(tickInterval);
          return max;
        }

        virtual void load()
        {
          if (!this->loaded())
          {
            this->m_first_page_in_nav_bar = 0;
            this->m_page_nav_bar->setLayout(this->create_page_nav_bar(), AlignTop | AlignJustify);
            this->m_slider->setValue(this->update_slider());

            this->m_slider->sliderMoved().connect(this, &WCustomTableViewImpl::slt_sliderMoved);

            this->m_nav_bar_created = true;
          }
          this->WContainerWidget::load();
        }

        int rowsPerPage() const
        {
          return this->m_rows_per_page;
        }

        void setRowsPerPage(const int rows)
        {
          this->m_rows_per_page = rows;
        }

        void slt_sliderMoved(int page_clicked)
        {
          bool nbnu = false;
//          if (this->pageCount() >= page_clicked)
//          {
            page_clicked = (int) (((this->m_slider->maximum() - page_clicked) / (this->m_slider->maximum() * 1.0)) * (this->pageCount() - 1));

            if (this->m_current_page != page_clicked)
            {
              if (page_clicked <= this->pageCount())
                this->scrollTo(
                      this->model()
                        ->index(page_clicked * this->rowsPerPage(), 0), WTableView::PositionAtTop);
              this->m_first_page_in_nav_bar = page_clicked;
              this->m_last_page_in_nav_bar = page_clicked + this->m_num_of_pages_in_nav_bar - 1;
              if ((this->m_first_page_in_nav_bar + this->m_num_of_pages_in_nav_bar) > this->pageCount())
              {
                this->m_first_page_in_nav_bar = this->pageCount() - this->m_num_of_pages_in_nav_bar;
                this->m_last_page_in_nav_bar = this->pageCount();
              }
              this->m_current_page = page_clicked;

              nbnu = true;
            }
//          }
//          else
//          {
//            this->update_slider();
//            this->m_slider->setValue(this->m_slider->maximum());
//          }
          this->update_page_nav_bar(nbnu);
        }

        dbo::QueryModel<Result> *model() const
        {
          return dynamic_cast<dbo::QueryModel<Result> *>(this->m_wtv->model());
        }

        void setModel(dbo::QueryModel<Result> *model)
        {

          if (model)
            model->layoutChanged().connect(this, &WCustomTableViewImpl::slt_modelLayoutChanged);
          this->m_wtv->setModel(model);
          int row_count = this->model()->rowCount();
          this->m_page_count_reminder = (row_count > this->rowsPerPage()) ? row_count % this->rowsPerPage() : 0;
          this->m_page_count = (int) (((row_count * 1.0)/ (this->rowsPerPage()) + ((this->m_page_count_reminder) > 0 ? 1 : 0)));
        }

        void slt_modelLayoutChanged()
        {
          //int sortColumn = this->sortColumn();
          //if (sortColumn > -1)
          //{
            //WString headerName = boost::any_cast<WString>(this->model()->headerData(sortColumn));
            //this->m_txt->setText(tr(MIR_TABLE_EXP_SEARCH).arg(headerName));
            //this->m_txt->enable();
            //this->m_sle->enable();
            //this->m_wcb->enable();
          //}
          //else
          //{
            //this->m_txt->setText(tr(MIR_TABLE_EXP_SEARCH).arg(WString::Empty));
            //this->m_txt->disable();
            //this->m_sle->disable();
            //this->m_wcb->disable();
          //}
          int row_count = this->model()->rowCount();
          this->m_page_count_reminder = (row_count > this->rowsPerPage()) ? row_count % this->rowsPerPage() : 0;
          this->m_page_count = (int) (((row_count * 1.0)/ (this->rowsPerPage()) + ((this->m_page_count_reminder) > 0 ? 1 : 0)));
        }

        void setSortingEnabled(int column, bool enabled)
        {
          this->m_wtv->setSortingEnabled(column, enabled);
        }

        virtual void setRowHeight(const WLength& rowHeight)
        {
          this->m_wtv->setRowHeight(rowHeight);
        }

        virtual void setColumnWidth(int column, const WLength& width)
        {
          this->m_wtv->setColumnWidth(column, width);
        }

        void sortByColumn(int column, SortOrder order)
        {
          this->m_wtv->sortByColumn(column, order);
        }

        virtual void setAlternatingRowColors(bool enable)
        {
          this->m_wtv->setAlternatingRowColors(enable);
        }

        void setItemDelegateForColumn(int column, WAbstractItemDelegate *delegate)
        {
          this->m_wtv->setItemDelegateForColumn(column, delegate);
        }

        virtual void setSelectable(bool selectable)
        {
          this->m_wtv->setSelectable(selectable);
        }

        virtual void setCheckable(const bool checkable)
        {
          this->m_checkable = checkable;
        }

        virtual void resize(const int& widthPx, const int& heightPx)
        {
          this->WContainerWidget::resize(widthPx, heightPx);
          this->m_wtv->resize(widthPx, heightPx-120);
          this->m_slider->setHeight(heightPx-120);
        }

        virtual void resize(const WLength& width, const WLength& height)
        {
          this->WContainerWidget::resize(width, height);
        }

        virtual void resize_inner_table_view(const WLength& width, const WLength& height)
        {
          this->m_wtv->resize(width, height);
        }

        virtual void scrollTo(const WModelIndex& index,
            Wt::WTableView::ScrollHint hint = Wt::WTableView::EnsureVisible)
        {
          this->m_wtv->scrollTo(index, hint);
        }

        virtual int pageCount() const
        {
          return this->m_page_count;//(this->model()->rowCount() / (this->rowsPerPage()));
        }

        virtual int pageCountReminder() const
        {
          return this->m_page_count_reminder;//(this->model()->rowCount() / (this->rowsPerPage()));
        }



        virtual int pageSize() const
        {
          return this->m_wtv->pageSize();
        }

        virtual void setPrefNumOfPagesInNavBar(int num_of_pages)
        {
          this->m_pref_num_of_pages_in_nav_bar = num_of_pages;
        }

        virtual int sortColumn()
        {
          return this->m_wtv->sortColumn();
        }

        virtual void refresh()
        {
          this->m_wtv->refresh();
        }

        void update_page_nav_bar(const bool navBarNeedUpdate = false)
        {
          int page_count = this->pageCount();
          if (navBarNeedUpdate && this->m_nav_bar_created)
          {
            //if (page_count < this->m_first_page_in_nav_bar + this->m_pref_num_of_pages_in_nav_bar - 1)
            if (page_count < this->m_first_page_in_nav_bar + this->m_num_of_pages_in_nav_bar - 1)
            {
              this->m_first_page_in_nav_bar = 0;
              this->m_num_of_pages_in_nav_bar = page_count - this->m_first_page_in_nav_bar;
              this->m_current_page = 0;
//              this->model()->reload();
              this->m_wtv
                ->scrollTo(
                    this->model()->index(0, 0), WTableView::PositionAtTop);
            }
            else
              this->m_num_of_pages_in_nav_bar = this->m_pref_num_of_pages_in_nav_bar;

            this->m_page_nav_bar->clear();
            this->m_page_numbers.clear();
            this->m_page_nav_bar->setLayout(this->create_page_nav_bar(), AlignTop | AlignJustify);
          }
          if ((this->m_page_numbers.size() > 0) && (this->m_page_numbers.at(0)->text().toUTF8() != boost::lexical_cast<string>(this->m_first_page_in_nav_bar + 1)))
          {
            int first_page_in_nav_bar = this->m_first_page_in_nav_bar;

            BOOST_FOREACH(WCustomTableViewPushButton<Result> *b, this->m_page_numbers)
            {
              b->setText(boost::lexical_cast<string>(first_page_in_nav_bar++ + 1));
            }
          }
          string current_page = boost::lexical_cast<string>(this->m_current_page + 1);
          BOOST_FOREACH(WCustomTableViewPushButton<Result> *b, this->m_page_numbers)
          {
            if (b->text().toUTF8() == current_page)
            {
              b->setStyleClass(MIR_STRCAT3(MIR_CSS_PAGE_NAV_BUTTON," ",MIR_CSS_PAGE_NAV_BUTTON_SELECTED));
            }
            else
            {
              b->setStyleClass(MIR_STRINGIFY(MIR_CSS_PAGE_NAV_BUTTON));
            }
          }

        }

      protected:

        virtual WHBoxLayout *create_page_nav_bar()
        {
          WHBoxLayout *layout = new WHBoxLayout();
          int page_count = this->pageCount();
          if (page_count > this->m_pref_num_of_pages_in_nav_bar)
            page_count = this->m_pref_num_of_pages_in_nav_bar;
          this->m_num_of_pages_in_nav_bar = page_count;
          if (this->pageCount() > 1)
          {
            WCustomTableViewPushButton<Result> *wpb = new WCustomTableViewPushButton<Result>(tr(MIR_WCTV_FIRST), this);
            this->m_wpbFirst = wpb;
            layout->addWidget(wpb);
            wpb->setStyleClass(MIR_STRINGIFY(MIR_CSS_PAGE_NAV_BUTTON));
            wpb->clicked().connect(wpb, &WCustomTableViewPushButton<Result>::slt_first_page_clicked);
            wpb = new WCustomTableViewPushButton<Result>(tr(MIR_WCTV_PREV), this);
            layout->addWidget(wpb);
            wpb->setStyleClass(MIR_STRINGIFY(MIR_CSS_PAGE_NAV_BUTTON));
            wpb->clicked().connect(wpb, &WCustomTableViewPushButton<Result>::slt_prev_page_clicked);
            wpb = new WCustomTableViewPushButton<Result>("<", this);
            layout->addWidget(wpb);
            wpb->setStyleClass(MIR_STRINGIFY(MIR_CSS_PAGE_NAV_BUTTON));
            wpb->clicked().connect(wpb, &WCustomTableViewPushButton<Result>::slt_prev_pages_clicked);

            for(int i = this->m_first_page_in_nav_bar; i < this->m_first_page_in_nav_bar + page_count; i++)
            {
              wpb = new WCustomTableViewPushButton<Result>(boost::lexical_cast<string>(i+1), this);
              layout->addWidget(wpb);
              wpb->setStyleClass(MIR_STRINGIFY(MIR_CSS_PAGE_NAV_BUTTON));
              wpb->clicked().connect(wpb, &WCustomTableViewPushButton<Result>::slt_page_clicked);
              this->m_page_numbers.push_back(wpb);
            }

            this->m_last_page_in_nav_bar = this->m_first_page_in_nav_bar + page_count - 1;

            wpb = new WCustomTableViewPushButton<Result>(">", this);
            layout->addWidget(wpb);
            wpb->setStyleClass(MIR_STRINGIFY(MIR_CSS_PAGE_NAV_BUTTON));
            wpb->clicked().connect(wpb, &WCustomTableViewPushButton<Result>::slt_next_pages_clicked);
            wpb = new WCustomTableViewPushButton<Result>(tr(MIR_WCTV_NEXT), this);
            layout->addWidget(wpb);
            wpb->setStyleClass(MIR_STRINGIFY(MIR_CSS_PAGE_NAV_BUTTON));
            wpb->clicked().connect(wpb, &WCustomTableViewPushButton<Result>::slt_next_page_clicked);
            wpb = new WCustomTableViewPushButton<Result>(tr(MIR_WCTV_LAST), this);
            layout->addWidget(wpb);
            wpb->setStyleClass(MIR_STRINGIFY(MIR_CSS_PAGE_NAV_BUTTON));
            wpb->clicked().connect(wpb, &WCustomTableViewPushButton<Result>::slt_last_page_clicked);
          }
          this->update_page_nav_bar();
          return layout;
        }

      private:
        WTableView                                   *m_wtv;

        DatabaseModule                               *m_dm;

        WContainerWidget                             *m_page_nav_bar;

        WLineEdit                                    *m_sle;

        WComboBox                                    *m_scb;

        WText                                        *m_txt_selected;

        WText                                        *m_txt_all_displayed;

        WPushButton                                  *m_wpb;

        WCheckBox                                    *m_show_only_selected;

        //WCheckBox                                    *m_wcb;

        WPushButton                                  *m_selectAll;

        WPushButton                                  *m_inverseSelection;

        WPushButton                                  *m_unselectAll;

        WSlider								                	     *m_slider;

        dbo::Session                                 *m_session;

        vector<WCustomTableViewPushButton<Result> *>  m_page_numbers;

        WCustomTableViewPushButton<Result>           *m_wpbFirst;
		
        bool                                          m_checkable;

        int                                           m_first_page_in_nav_bar;

        int                                           m_last_page_in_nav_bar;

        int                                           m_current_page;

        int                                           m_num_of_pages_in_nav_bar;

        int                                           m_pref_num_of_pages_in_nav_bar;

        int                                           m_rows_per_page;

        bool                                          m_nav_bar_created;

        int                                           m_page_count;

        int                                           m_page_count_reminder;

        WCustomTableView<Result>                     *m_customTableView;
    };

    template <class Result>
    class WCustomTableView : public WCompositeWidget
    {
      public:
        WCustomTableView(DatabaseModule *dm, dbo::Session *session, bool likeStatement = false, WContainerWidget *parent = 0)
          : WCompositeWidget(parent)
        {
          impl_ = new WCustomTableViewImpl<Result>(dm, session, likeStatement, this);
          setImplementation(impl_);        
        }

        WCustomTableViewImpl<Result> *impl()
        {
          return this->impl_;
        }

      private:
        WCustomTableViewImpl<Result> *impl_;
    };

  }
}
#endif /* _MIR_WCUSTOMTABLEVIEW_H_ */
