unit GraphOutsideGrouped;
  (* wind range as arrow series *)

  interface
    uses
      AdsData       (* used by AdsFunc *),
      AdsFunc       (* used by AdsTable *),
      AdsTable      (* tAdsQuery *),
      ArrowCha      (* tArrowSeries *),
      Chart         (* tChart *),
      Classes       (**),
      Controls      (* tControl, crHourglass, tCursor, tDate *),
      DB            (**),
      DBChart       (* tDBChart *),
      ExtCtrls      (* used by DBChart *),
      Forms         (* tForm, Screen *),
      Menus         (* tMainMenu, tMenuItem *),
      Messages      (* tMessage, WM_ExitSizeMove *),
      PickDateRange (* tDateRange, fmPickDateRange *),
      ScaleManager  (* ArrangeAxisAndTitle, tCelsiusScale, tScale, ShowAxes *),
      Series        (* tBarSeries, tLineSeries *),
      TeEngine      (* tChartAxis; also used by Chart *),
      TeeProcs      (* DateTimeStep; also used by Chart *),
      Windows;

    type
      tfmGroupedOutsideGraph = class(tForm)
        qSummaryData: tAdsQuery;
          qSummaryDataTheDate: tDateField;
          qSummaryDataHighTemp: tFloatField;
          qSummaryDataLowTemp:  tFloatField;
          qSummaryDataRainmm:   tFloatField;
          qSummaryDataSunrise:  tFloatField;
          qSummaryDataSunset:   tFloatField;
          qSummaryDataScaledACPercent:      tIntegerField;
          qSummaryDataScaledFurnacePercent: tIntegerField;
        dbChartGroupedOutside: tDBChart;
          seriesPhotoPeriod: tArrowSeries;
          seriesTempF:       tArrowSeries;
          seriesAC:      tBarSeries;
          seriesFurnace: tBarSeries;
          seriesRain:    tBarSeries;
        menuMain: tMainMenu;
          menuDateRange: tMenuItem;
          menuGrid:      tMenuItem;
          menuPlot:      tMenuItem;
          menuPrint:     tMenuItem;
          menuSeries:    tMenuItem;
            menuSeriesACandFurnace: tMenuItem;
            menuSeriesPhotoperiod:  tMenuItem;
            menuSeriesRain:         tMenuItem;
            menuSeriesTempRange:    tMenuItem;

          procedure FormCreate        (Sender: tObject);
          procedure FormDblClick      (Sender: tObject);
          procedure FormDestroy       (Sender: tObject);
          procedure FormShow          (Sender: tObject);
          procedure menuDateRangeClick(Sender: tObject);
          procedure menuGridClick     (Sender: tObject);
          procedure menuPlotClick     (Sender: tObject);
          procedure menuPrintClick    (Sender: tObject);
        private
          LeftCelsiusScale: tCelsiusScale;
          TheDateRange: tDateRange;
          procedure WMExitSizeMove(var TheMessage: tMessage);
                                        message WM_ExitSizeMove;
        public
          procedure Initialize;
        end (* tfmGroupedOutsideGraph *);

    var
      fmGroupedOutsideGraph: tfmGroupedOutsideGraph;

  implementation
    uses
      DateUtils     (* DateOf, DaysBetween *),
      Dialogs       (* ShowMessageFmt *),
      Graphics      (* clRed *),
      HC4Main       (* fmHC4Main *),
      HC3Utilities  (* FtoC, LastSecondOfTheDay *),
      LabelAdjuster (* ResizeText *),
      MyConsts      (* nullstring *),
      MyUtils       (* ResizeText *),
      SysUtils      (* Format, FormatDateTime *),
      TableManager  (* tuPowerLog *);
      {$R *.dfm}

    procedure tfmGroupedOutsideGraph.FormCreate(Sender: tObject);
      (* Initialize non-VCL components *)

      begin (* tfmGroupedOutsideGraph.FormCreate *)
        TheDateRange := tDateRange.Create('grouped outside date range',
                                          dbChartGroupedOutside)
      end   (* tfmGroupedOutsideGraph.FormCreate *);

    procedure tfmGroupedOutsideGraph.FormDblClick(Sender: tObject);
      (* show mother form *)

      begin (* tfmGroupedOutsideGraph.FormDblClick *)
        fmHC4Main.Show
      end   (* tfmGroupedOutsideGraph.FormDblClick *);

    procedure tfmGroupedOutsideGraph.FormDestroy(Sender: tObject);
      (* clean up *)

      begin (* tfmGroupedOutsideGraph.FormDestroy *)
        TheDateRange.Free
      end   (* tfmGroupedOutsideGraph.FormDestroy *);

    procedure tfmGroupedOutsideGraph.FormShow(Sender: tObject);
      (* initialize *)
    
      begin (* tfmGroupedOutsideGraph.FormShow *)
        menuSeriesACandFurnace.Checked := true;
        menuSeriesPhotoperiod.Checked := true;
        menuSeriesRain.Checked := true;
        menuSeriesTempRange.Checked := true;

        menuPlotClick(nil)
      end   (* tfmGroupedOutsideGraph.FormShow *);

    procedure tfmGroupedOutsideGraph.Initialize;
      (* one-time setup *)
    
      begin (* tfmGroupedOutsideGraph.Initialize *)
        LeftCelsiusScale := tCelsiusScale.Create(dbChartGroupedOutside.LeftAxis);
        LeftCelsiusScale.AdjustLimits
      end   (* tfmGroupedOutsideGraph.Initialize *);

    procedure tfmGroupedOutsideGraph.menuDateRangeClick(Sender: tObject);
      (* xxx does all the work *)
    
      begin (* tfmGroupedOutsideGraph.menuDateRangeClick *)
        TheDateRange.PickRange;
        menuPlot.Enabled := true;
        with qSummaryData do
          begin (* qSummaryData *)
            Close;
            ParamByName('DomainBegin').AsDate := TheDateRange.DomainBegin;
            ParamByName('DomainEnd'  ).AsDate := TheDateRange.DomainEnd;
            Open
          end   (* qSummaryData *)
      end   (* tfmGroupedOutsideGraph.menuDateRangeClick *);

    procedure tfmGroupedOutsideGraph.menuGridClick(Sender: tObject);
      (* °F/°C toggle *)
    
      begin (* tfmGroupedOutsideGraph.menuGridClick *)
        menuGrid.Caption := LeftCelsiusScale.ToggleGrid('grid°F', 'grid°C');
        menuPlotClick(nil)
      end   (* tfmGroupedOutsideGraph.menuGridClick *);

    procedure tfmGroupedOutsideGraph.menuPlotClick(Sender: tObject);
      (* really do it  SetTitleAndColors is needed here because the
      LeftAxis.Title.Caption was null when the LeftCelsiusScale was created *)
    
      const
        clFahrenheit      = clRed;
        InnerAxisPosition =  -1;
        VOutmostMargin    = -50;

      var
        OldCursor: tCursor;
        LeftEdge: integer;

      procedure PrepareSeries(TheSeries: tChartSeries;
                              TheMenuItem: tMenuItem;
                              TheAxis: tChartAxis;
                              const TheLegend: string);
        (* activate the series, or don't *)

        begin (* PrepareSeries *)
          TheSeries.Active := TheMenuItem.Checked;
          if TheMenuItem.Checked then
            with TheAxis.Title do
              if Caption = nullstring then
                Caption := TheLegend
              else
                Caption := Format('%s, %s', [Caption, TheLegend])
        end   (* PrepareSeries *);

      procedure SetXAxisScale(TheDateRange: tDateRange; TheGraph: tDBChart);
        (* If the date range is more than a few days, scale should be by date.
         Otherwise, it can be by hour. *)

        const
          FewDays = 3;
          OneDay = 1.0;
          OneHour = OneDay / 24.0;

        var
          Days: integer;

        begin (* SetXAxisScale *)
          with TheDateRange do
            Days := DaysBetween(DomainEnd, DomainBegin) + 1;

          with TheGraph.BottomAxis do
            begin (* organize bottom axis *)
              AutomaticMaximum := false;
              AutomaticMinimum := false;
              Minimum := 0;
              Maximum := LastSecondOfTheDay(TheDateRange.DomainEnd);
              Minimum := TheDateRange.DomainBegin;
              case Days of
                0 .. FewDays: begin (* short time *)
                                Increment := OneHour;
                                DateTimeFormat := 'hhmm'
                              end   (* short time *)

                         else begin (* longer *)
                                Increment := OneDay;
                                DateTimeFormat := 'M/d'
                              end   (* longer *)
                end (* case *)
            end   (* organize bottom axis *)
        end   (* SetXAxisScale *);

      begin (* tfmGroupedOutsideGraph.menuPlotClick *)
        OldCursor := Screen.Cursor;
        Screen.Cursor := crHourglass;

        with dbChartGroupedOutside do
          try     (* restore cursor *)
            LeftAxis.Title.Caption  := nullstring;
            RightAxis.Title.Caption := nullstring;
            SetXAxisScale(TheDateRange, dbChartGroupedOutside);
            PrepareSeries(seriesTempF,       menuSeriesTempRange,      LeftAxis,  'temp range (°F)');
            PrepareSeries(seriesAC,          menuSeriesACandFurnace,   LeftAxis,  'A/C'            );
            PrepareSeries(seriesFurnace,     menuSeriesACandFurnace,   LeftAxis,  'furnace'        );
            PrepareSeries(seriesRain,        menuSeriesRain,           LeftAxis,  'rain (mm)'      );
            PrepareSeries(seriesPhotoPeriod, menuSeriesPhotoperiod,    RightAxis, 'photoperiod'    );
            
            self.Caption := FormatDateTime('"grouped outside graph plotted "m/d" @ "hhnn', Now);

            with LeftCelsiusScale, dbChartGroupedOutside do
              begin (* LeftCelsiusScale, dbChartGroupedOutside *)
                LeftCelsiusScale.SetTitleAndColors(LeftAxis, LeftAxis.Title.Caption, clFahrenheit);
                if Inner then
                  LeftEdge := ShowAxes(LeftCelsiusScale, LeftAxis, InnerAxisPosition)
                else
                  LeftEdge := ShowAxes(LeftAxis, LeftCelsiusScale, InnerAxisPosition);
                LeftAxis.Title.Distance := LeftAxis.Title.Distance - 20;   // empiric
                // ShowMessageFmt('LeftAxis.Title.Distance = %d', [LeftAxis.Title.Distance]);
                MarginLeft := -LeftEdge + VOutmostMargin
              end   (* LeftCelsiusScale, dbChartGroupedOutside *);
          finally (* restore cursor *)
            Screen.Cursor := OldCursor
          end     (* restore cursor *)
      end   (* tfmGroupedOutsideGraph.menuPlotClick *);

    procedure tfmGroupedOutsideGraph.menuPrintClick(Sender: tObject);
      (* just print it *)

      begin (* tfmGroupedOutsideGraph.menuPrintClick *)
        dbChartGroupedOutside.PrintLandscape
      end   (* tfmGroupedOutsideGraph.menuPrintClick *);

    procedure tfmGroupedOutsideGraph.WMExitSizeMove(var TheMessage: tMessage);
      (* After resizing, adjust the legends *)

      const
        MinFontSize =  5;
        MaxFontSize = 13;

      var
        CaptionLimit: integer;

      begin (* tfmGroupedOutsideGraph.WMExitSizeMove *)
        with dbChartGroupedOutside do
          begin (* TheChart *)
            CaptionLimit := 9 * Height div 10;
            ResizeText(LeftAxis.Title.Caption,  LeftAxis.Title.Font,
                         CaptionLimit, MinFontSize, MaxFontSize);
            ResizeText(RightAxis.Title.Caption, RightAxis.Title.Font,
                         CaptionLimit, MinFontSize, LeftAxis.Title.Font.Size)
          end   (* TheChart *)
      end   (* tfmGroupedOutsideGraph.WMExitSizeMove *);


end.
