/// \file
/// \ingroup tutorial_gui
/// This macro gives an example of how to use html widget to display tabular data.
/// To run it do either:
/// ~~~
/// .x calendar.C
/// .x calendar.C++
/// ~~~
///
/// \macro_code
///
/// \author Valeriy Onuchin   24/08/2007

#include "TDatime.h"
#include "TTimeStamp.h"
#include "TGComboBox.h"
#include "TGNumberEntry.h"
#include "TGLabel.h"
#include "TGColorSelect.h"
#include "TGHtml.h"
#include "TApplication.h"
#include "TROOT.h"
#include "TColor.h"

/////////////////////////// HTML calendar //////////////////////////////////////

TString monthNames[12] = {"January", "February", "March",     "April",   "May",      "June",
                          "July",    "August",   "September", "October", "November", "December"};

////////////////////////////////////////////////////////////////////////////////
class HtmlDayName {
public:                // make them public for shorter code
   TString fDay;       // day name, e.g. "Sunday"
   TString fAlign;     // name align inside table cell
   TString fBgColor;   // cell background color
   TString fFontSize;  // text font size
   TString fFontColor; // text color
   TString fHtml;      // HTML output code

public:
   HtmlDayName(const char *day);
   virtual ~HtmlDayName() {}

   TString Html() const { return fHtml; }

   ClassDef(HtmlDayName, 0);
};

//______________________________________________________________________________
HtmlDayName::HtmlDayName(const char *day)
   : fDay(day), fAlign("middle"), fBgColor("#000000"), fFontSize("4"), fFontColor("#FFFFFF")
{
   // ctor.

   fHtml += "<TH  width=14%";
   fHtml += " align=" + fAlign;
   fHtml += " bgcolor=" + fBgColor + ">";
   fHtml += "<font size=" + fFontSize;
   fHtml += " color=" + fFontColor + ">";
   fHtml += fDay;
   fHtml += "</font></TH>\n";
}

////////////////////////////////////////////////////////////////////////////////
class HtmlMonthTable {
public:          // make them public for shorter code
   Int_t fYear;  // year
   Int_t fMonth; // month

   TString fBorder;       // border width
   TString fBgColor;      // background color
   TString fCellpadding;  // cell padding
   TString fCellFontSize; // cell font size
   TString fCellBgcolor;  // cell background color
   TString fTodayColor;   // background color of cell correspondent today date

   TDatime fToday; // today's date
   TString fHtml;  // HTML output code

   void Build();
   void BuildDayNames();
   void BuildDays();

public:
   HtmlMonthTable(Int_t year, Int_t month);
   virtual ~HtmlMonthTable() {}

   void SetDate(Int_t year, Int_t month);
   TString Html() const { return fHtml; }

   ClassDef(HtmlMonthTable, 0);
};

//______________________________________________________________________________
HtmlMonthTable::HtmlMonthTable(Int_t year, Int_t month)
   : fYear(year),
     fMonth(month),
     fBorder("2"),
     fBgColor("#aaaaaa"),
     fCellpadding("5"),
     fCellFontSize("3"),
     fCellBgcolor("#eeeeee"),
     fTodayColor("#ffff00")
{
   // Constructor.

   Build();
}

//______________________________________________________________________________
void HtmlMonthTable::SetDate(Int_t year, Int_t month)
{
   // Set date.

   fYear = year;
   fMonth = month;
   Build();
}

//______________________________________________________________________________
void HtmlMonthTable::Build()
{
   // Build HTML code.

   fHtml = "<TABLE width=100%";
   fHtml += " border=" + fBorder;
   fHtml += " bgcolor=" + fBgColor;
   fHtml += " cellpadding=" + fCellpadding;
   fHtml += "><TBODY>";

   BuildDayNames();
   BuildDays();

   fHtml += "</TBODY></TABLE>\n";
}

//______________________________________________________________________________
void HtmlMonthTable::BuildDayNames()
{
   // Build table header with day names.

   fHtml += "<TR>";
   fHtml += HtmlDayName("Sunday").Html();
   fHtml += HtmlDayName("Monday").Html();
   fHtml += HtmlDayName("Tuesday").Html();
   fHtml += HtmlDayName("Wednesday").Html();
   fHtml += HtmlDayName("Thursday").Html();
   fHtml += HtmlDayName("Friday").Html();
   fHtml += HtmlDayName("Saturday").Html();
   fHtml += "</TR>\n";
}

//______________________________________________________________________________
void HtmlMonthTable::BuildDays()
{
   // Build part of table with day numbers.

   static Int_t maxdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

   Int_t maxday = maxdays[fMonth - 1];
   if ((fMonth == 2) && TTimeStamp::IsLeapYear(fYear))
      maxday = 29;

   Int_t first = TTimeStamp::GetDayOfWeek(1, fMonth, fYear);

   // fill html table
   for (int week = 0; week < 6; week++) {
      fHtml += "<TR>";

      for (int weekday = 0; weekday < 7; weekday++) { //
         Int_t day = week * 7 + weekday - first + 1;

         if ((day > maxday) && !weekday)
            break; //

         fHtml += "<TD align=left width=14% ";

         // hightlight today's cell
         if ((fToday.GetYear() == fYear) && (fToday.GetMonth() == fMonth) && (fToday.GetDay() == day)) {
            fHtml += " bgcolor=" + fTodayColor;
         } else {
            fHtml += " bgcolor=" + fCellBgcolor;
         }
         fHtml += ">";

         // skip week days which are not of this month
         if ((day <= 0) || (day > maxday)) {
            fHtml += "&nbsp;</TD>";
            continue;
         }

         fHtml += "<font size=" + fCellFontSize + ">";
         fHtml += Form("%d", day);
         fHtml += "</font></TD>\n";
      }
      fHtml += "</TR>\n";
   }
}

////////////////////////////////////////////////////////////////////////////////
class HtmlCalendar {
public:                        // make them public for shorter code
   Int_t fYear;                // year
   Int_t fMonth;               // month
   HtmlMonthTable fMonthTable; // HTML table presenting month days
   TString fHeader;            // HTML header
   TString fFooter;            // HTML footer
   TString fHtml;              // output HTML string
   TString fTitle;             // page title

   void MakeHeader();
   void MakeFooter();

public:
   HtmlCalendar(Int_t year, Int_t month);
   virtual ~HtmlCalendar() {}

   void SetDate(Int_t year, Int_t month);
   TString Html() const { return fHtml; }

   ClassDef(HtmlCalendar, 0);
};

//______________________________________________________________________________
HtmlCalendar::HtmlCalendar(Int_t year, Int_t month) : fMonthTable(year, month)
{
   // Constructor.

   fYear = year;
   fMonth = month;

   MakeHeader();
   MakeFooter();

   fHtml = fHeader;
   fHtml += fMonthTable.Html();
   fHtml += fFooter;
}

//______________________________________________________________________________
void HtmlCalendar::SetDate(Int_t year, Int_t month)
{
   // Create calendar for month/year.

   fYear = year;
   fMonth = month;

   fMonthTable.SetDate(year, month);
   MakeHeader();
   MakeFooter();
   fHtml = fHeader;
   fHtml += fMonthTable.Html();
   fHtml += fFooter;
}

//______________________________________________________________________________
void HtmlCalendar::MakeHeader()
{
   // Make HTML header.

   fTitle = monthNames[fMonth - 1] + Form(" %d", fYear);
   fHeader = "<html><head><title>";
   fHeader += fTitle;
   fHeader += "</title></head><body>\n";
   fHeader += "<center><H2>" + fTitle + "</H2></center>";
}

//______________________________________________________________________________
void HtmlCalendar::MakeFooter()
{
   // Make HTML footer.

   fFooter = "<br><p><br><center><strong><font size=2 color=#2222ee>";
   fFooter += "Example of using Html widget to display tabular data.";
   fFooter += "</font></strong></center></body></html>";
}

//////////////////////// end of HTML calendar //////////////////////////////////

class CalendarWindow {
private:
   TGMainFrame *fMain;         // main frame
   HtmlCalendar *fHtmlText;    // calendar HTML table
   TGHtml *fHtml;              // html widget to display HTML calendar
   TGComboBox *fMonthBox;      // month selector
   TGNumberEntry *fYearEntry;  // year selector
   TGNumberEntry *fFontEntry;  // font size selector
   TGColorSelect *fTableColor; // selector of background color of table
   TGColorSelect *fCellColor;  // selector of background color of table's cells

public:
   CalendarWindow();
   virtual ~CalendarWindow();

   void UpdateHTML();

   ClassDef(CalendarWindow, 0);
};

//______________________________________________________________________________
CalendarWindow::~CalendarWindow()
{
   // Destructor.

   delete fHtmlText;
   delete fMain;
}

//______________________________________________________________________________
CalendarWindow::CalendarWindow()
{
   // Main  window.

   fMain = new TGMainFrame(gClient->GetRoot(), 10, 10, kVerticalFrame);
   fMain->SetCleanup(kDeepCleanup); // delete all subframes on exit

   // Controls
   TGHorizontalFrame *controls = new TGHorizontalFrame(fMain);
   fMain->AddFrame(controls, new TGLayoutHints(kLHintsCenterX, 1, 1, 1, 1));

   // generate HTML calendar table
   TDatime today;
   fHtmlText = new HtmlCalendar(today.GetYear(), today.GetMonth());

   // create HTML widget
   fHtml = new TGHtml(fMain, 1, 1);
   fMain->AddFrame(fHtml, new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 5, 5, 2, 2));

   // parse HTML context of HTML calendar table
   fHtml->ParseText((char *)fHtmlText->Html().Data());

   TGLabel *dateLabel = new TGLabel(controls, "Date:");
   controls->AddFrame(dateLabel, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, 5, 2, 2, 2));

   //
   fMonthBox = new TGComboBox(controls);
   for (int i = 0; i < 12; i++) {
      fMonthBox->AddEntry(monthNames[i].Data(), i + 1);
   }
   fMonthBox->Select(today.GetMonth());
   controls->AddFrame(fMonthBox, new TGLayoutHints(kLHintsLeft, 5, 5, 2, 2));

   fYearEntry = new TGNumberEntry(controls, today.GetYear(), 5, -1, TGNumberFormat::kNESInteger,
                                  TGNumberFormat::kNEAPositive, TGNumberFormat::kNELLimitMin, 1995);
   controls->AddFrame(fYearEntry, new TGLayoutHints(kLHintsLeft, 5, 5, 2, 2));

   fMonthBox->Resize(100, fYearEntry->GetHeight());

   TGLabel *fontLabel = new TGLabel(controls, "Font Size:");
   controls->AddFrame(fontLabel, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, 30, 2, 2, 2));

   Int_t fontsize = atoi(fHtmlText->fMonthTable.fCellFontSize.Data());
   fFontEntry = new TGNumberEntry(controls, fontsize, 2, -1, TGNumberFormat::kNESInteger, TGNumberFormat::kNEAPositive,
                                  TGNumberFormat::kNELLimitMax, 0, 7);
   controls->AddFrame(fFontEntry, new TGLayoutHints(kLHintsLeft, 5, 5, 2, 2));

   TGLabel *tableLabel = new TGLabel(controls, "Table:");
   controls->AddFrame(tableLabel, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, 5, 2, 2, 2));

   Pixel_t color;

   gClient->GetColorByName(fHtmlText->fMonthTable.fBgColor.Data(), color);
   fTableColor = new TGColorSelect(controls, color);
   controls->AddFrame(fTableColor, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, 5, 2, 2, 2));

   TGLabel *cellLabel = new TGLabel(controls, "Cell:");
   controls->AddFrame(cellLabel, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, 5, 2, 2, 2));

   gClient->GetColorByName(fHtmlText->fMonthTable.fCellBgcolor.Data(), color);
   fCellColor = new TGColorSelect(controls, color);
   controls->AddFrame(fCellColor, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, 5, 2, 2, 2));

   // connect signals
   fMonthBox->Connect("Selected(Int_t)", "CalendarWindow", this, "UpdateHTML()");
   fYearEntry->GetNumberEntry()->Connect("TextChanged(char*)", "CalendarWindow", this, "UpdateHTML()");
   fFontEntry->GetNumberEntry()->Connect("TextChanged(char*)", "CalendarWindow", this, "UpdateHTML()");
   fTableColor->Connect("ColorSelected(Pixel_t)", "CalendarWindow", this, "UpdateHTML()");
   fCellColor->Connect("ColorSelected(Pixel_t)", "CalendarWindow", this, "UpdateHTML()");

   // terminate ROOT session when window is closed
   fMain->Connect("CloseWindow()", "TApplication", gApplication, "Terminate()");
   fMain->DontCallClose();

   fMain->MapSubwindows();
   fMain->Resize(600, 333);

   // set  minimum size of main window
   fMain->SetWMSizeHints(controls->GetDefaultWidth(), fMain->GetDefaultHeight(), 1000, 1000, 0, 0);

   TString title = "Calendar for ";
   title += fHtmlText->fTitle;
   fMain->SetWindowName(title.Data());
   fMain->MapRaised();
}

//______________________________________________________________________________
void CalendarWindow::UpdateHTML()
{
   // Update HTML table on user's input.

   Int_t month = fMonthBox->GetSelected();
   Int_t year = atoi(fYearEntry->GetNumberEntry()->GetText());
   fHtmlText->fMonthTable.fCellFontSize = fFontEntry->GetNumberEntry()->GetText();

   Pixel_t pixel = 0;
   TColor *color = nullptr;

   // table background
   pixel = fTableColor->GetColor();
   color = gROOT->GetColor(TColor::GetColor(pixel));

   if (color) {
      fHtmlText->fMonthTable.fBgColor = color->AsHexString();
   }

   // cell background
   pixel = fCellColor->GetColor();
   color = gROOT->GetColor(TColor::GetColor(pixel));

   if (color) {
      fHtmlText->fMonthTable.fCellBgcolor = color->AsHexString();
   }

   // update HTML context
   fHtmlText->SetDate(year, month);

   // parse new HTML context of HTML calendar table
   fHtml->Clear();
   fHtml->ParseText((char *)fHtmlText->Html().Data());
   fHtml->Layout();

   // update window title
   TString title = "Calendar for ";
   title += fHtmlText->fTitle;
   fMain->SetWindowName(title.Data());
}

////////////////////////////////////////////////////////////////////////////////
void calendar()
{
   // Main program.

   new CalendarWindow();
}
