(************************************************************************
 *                                                                      *
 *   Ant Movie Catalog 4.x                                              *
 *   (C) 2000-2011 Antoine Potten, Mickaël Vanneufville                 *
 *   http://www.antp.be/software                                        *
 *                                                                      *
 ************************************************************************
 *                                                                      *
 *   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.                       *
 *                                                                      *
 ************************************************************************)

unit stats;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, ComCtrls, MovieClass,
  TeeProcs, TeEngine, Chart, ActnList, ImgList, Menus, Series, Contnrs,

  AntCorelButton, AntStringList, TB2Item, TBX, TB2Dock, TB2Toolbar,

  base, AntAutoHintLabel, frameincludemov;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

const
  statItemGeneral         =  0;
  statItemAdditions       =  1;
  statItemMediaType       =  2;
  statItemSource          =  3;
  statItemCountries       =  4;
  statItemCategories      =  5;
  statItemYears           =  6;
  statItemLengths         =  7;
  statItemVideoFormats    =  8;
  statItemAudioFormats    =  9;
  statItemFramerates      = 10;
  statItemLanguages       = 11;
  statItemSubtitles       = 12;
  statItemDirectors       = 13;
  statItemProducers       = 14;
  statItemActors          = 15;

type

  TStatsWin = class(TBaseDlg)
    Panel1: TPanel;
    ActionList1: TActionList;
    ActionCopy: TAction;
    ActionSaveAs: TAction;
    ActionOptions: TAction;
    ActionOptionsLegend: TAction;
    ActionOptionsLabels: TAction;
    lstGeneral: TListView;
    TheChart: TChart;
    Series1: TPieSeries;
    Series2: TBarSeries;
    ActionOptionsEmpty: TAction;
    TBDock1: TTBXDock;
    ToolbarGraphOptions: TTBXToolbar;
    tbbSaveAs: TTBXItem;
    tbs1: TTBXSeparatorItem;
    tbbOptions: TTBXSubmenuItem;
    tbbOptionsEmpty: TTBXItem;
    tbbOptionsLabel: TTBXItem;
    tbbOptionsLegend: TTBXItem;
    ActionCopyWMF: TAction;
    ActionCopyBMP: TAction;
    tbbCopy: TTBXSubmenuItem;
    tbbCopyMetafile: TTBXItem;
    tbbCopyBitmap: TTBXItem;
    Messages: TAntStringList;
    ActionOptionsGroup: TAction;
    tbbOptionsGroup: TTBXItem;
    Panel2: TPanel;
    lstCategories: TListBox;
    IncMovies: TIncludemovFrame;
    procedure FormShow(Sender: TObject);
    procedure ActionOptionsExecute(Sender: TObject);
    procedure lstCategoriesClick(Sender: TObject);
    procedure ActionSaveAsExecute(Sender: TObject);
    procedure ActionOptionsLegendExecute(Sender: TObject);
    procedure ActionOptionsLabelsExecute(Sender: TObject);
    procedure ActionCopyWMFExecute(Sender: TObject);
    procedure ActionCopyBMPExecute(Sender: TObject);
    procedure ActionCopyExecute(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    MovieList: TMovieList;
    CatalogPath: TFileName;
    procedure OnDialogTypeChange(Sender: TObject);
  protected
    procedure LoadOptions; override;
    procedure SaveOptions; override;
  public
    procedure Execute(const MovieList: TMovieList; const CatalogPath: TFileName);
    procedure Translate; override;
  end;

  TStatItems = set of byte;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

var
  StatsWin: TStatsWin;

  statItemsPie: TStatItems = [statItemMediaType..statItemActors] - [statItemLengths, statItemYears];
  statItemsChart: TStatItems = [statItemAdditions, statItemLengths, statItemYears];
  statItems: TStatItems = [statItemAdditions..statItemActors];

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

implementation

uses
  CommDlg, DateUtils,

  PNGImage,
  
  fields, ConstValues, Global, ProgramSettings;

{$R *.dfm}

const
  msgMinutes    =  0;
  msgNone       =  1;
  msgSave       =  2;
  msgDayHourMin =  3;
  msgOthers     =  4;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TStatsWin.LoadOptions;
begin
  inherited;
  with Settings do
  begin
    with rStatistics do
    begin
      case WindowState of
        1:
          begin
            self.WindowState := wsNormal;
            self.Width := WindowWidth;
            self.Height := WindowHeight;
          end;
        2:
          begin
            self.WindowState := wsMaximized;
          end;
        else
          begin
            self.WindowState := wsNormal;
          end;
      end;
      ActionOptionsEmpty.Checked := EmptyMonths;
      ActionOptionsLegend.Checked := Legend;
      ActionOptionsLabels.Checked := Labels;
      ActionOptionsGroup.Checked := Group;
      IncMovies.ItemIndex := TMovieIncludeOption(Includemov);
    end;
    ToolbarGraphOptions.Images := ToolbarImages;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TStatsWin.SaveOptions;
begin
  inherited;
  with Settings.rStatistics do
  begin
    case self.WindowState of
      wsNormal:
        begin
          WindowState := 1;
          WindowWidth := Width;
          WindowHeight := Height;
        end;
      wsMaximized:
        begin
          WindowState := 2;
        end;
    end;
    EmptyMonths := ActionOptionsEmpty.Checked;
    Legend := ActionOptionsLegend.Checked;
    Labels := ActionOptionsLabels.Checked;
    Group := ActionOptionsGroup.Checked;
    Includemov := Integer(IncMovies.ItemIndex);
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TStatsWin.FormShow(Sender: TObject);
begin
  inherited;
  lstCategoriesClick(Self);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TStatsWin.Execute(const MovieList: TMovieList; const CatalogPath: TFileName);
begin
  Self.MovieList := MovieList;
  Self.CatalogPath := CatalogPath;
  IncMovies.SetCount(MovieList);
  ShowModal;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TStatsWin.ActionOptionsExecute(Sender: TObject);
begin
//
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TStatsWin.lstCategoriesClick(Sender: TObject);
var
  idx, i, field, valueidx, increment: Integer;
  labels: TStringList;
  fieldValue: string;
  firstDate, lastDate: TDateTime;
  firstValue, lastValue: Integer;
  group: Boolean;
  statCount: Integer;
  statLength: Double;
  statTotalLength: Integer;
  statTotalSize: Int64;
  statTotalDiscs: Integer;
  IncOpt: TMovieIncludeOption;
  Groups: TStringList;
  GroupMovies: TObjectList;
  GroupName: string;

begin
  IncOpt := IncMovies.ItemIndex;
  field := -1;
  increment := 1;
  idx := lstCategories.ItemIndex;
  if idx in statItems then
  begin
    TheChart.BringToFront;
    TheChart.Title.Text.Text := lstCategories.Items[idx] + sLineBreak + ExtractFileName(CatalogPath);
  end
  else
    lstGeneral.BringToFront;
  ActionCopy.Enabled := idx in statItems;
  ActionSaveAs.Enabled := idx in statItems;
  ActionOptions.Enabled := idx in statItems;
  ActionOptionsLegend.Enabled := idx in statItems;
  ActionOptionsLabels.Enabled := idx in statItemsPie;
  ActionOptionsEmpty.Enabled := idx = statItemAdditions;
  ActionOptionsGroup.Enabled := idx in ([statItemYears, statItemLengths] + statItemsPie);
  TheChart.SeriesList.Series[0].Active := idx in statItemsChart;
  TheChart.SeriesList.Series[1].Active := idx in statItemsPie;
  if MovieList = nil then
    Exit;
  if idx in statItemsChart then
  begin
    ActionOptionsLegendExecute(self);
    TheChart.Series[0].Clear;
    with TStringList.Create do
    try
      case idx of
        statItemAdditions:
          begin
            MovieList.Sort(fieldDate);
            if ActionOptionsEmpty.Checked then
            begin
              firstDate := 0;
              for i := 0 to MovieList.Count-1 do
                with TMovie(MovieList.Items[i]) do
                  if CanInclude(IncOpt) then
                    if iDate > 0 then
                    begin
                      firstDate := iDate;
                      break;
                    end; // if
              if firstDate > 0 then
              begin
                lastDate := recodeDay(TMovie(MovieList.Items[MovieList.Count-1]).iDate, 1);
                firstDate := recodeDay(firstDate, 1);
                while firstDate <= lastDate do
                begin
                  AddObject(FormatDateTime('mmm yyyy', firstDate), pointer(0));
                  firstDate := IncMonth(firstDate);
                end; // while
              end; // if > 0
            end; // if checked
            for i := 0 to MovieList.Count-1 do
              with TMovie(MovieList.Items[i]) do
                if CanInclude(IncOpt) then
                  if iDate > 0 then
                  begin
                    fieldValue := FormatDateTime('mmm yyyy', iDate);
                    valueidx := IndexOf(fieldValue);
                    if valueidx > -1 then
                      Objects[valueidx] := pointer(integer(Objects[valueidx]) + 1)
                    else
                    if not ActionOptionsEmpty.Checked then
                      AddObject(fieldValue, pointer(1));
                  end; // if > 0
            for i := 0 to Count-1 do
              TheChart.Series[0].Add(integer(Objects[i]), Strings[i]);
          end; // statItemAdditions
        statItemYears,
        statItemLengths:
          begin
            group := ActionOptionsGroup.Checked;
            case idx of
              statItemYears:
                begin
                  MovieList.Sort(fieldYear);
                  field := fieldYear;
                  increment := 5;
                end;
              statItemLengths:
                begin
                  MovieList.Sort(fieldLength);
                  field := fieldLength;
                  increment := 10;
                end;
            end; // case
            if group then
            begin
              firstValue := -1;
              for i := 0 to MovieList.Count-1 do
                with TMovie(MovieList.Items[i]) do
                  if CanInclude(IncOpt) then
                    if GetIntFieldValue(field) <> -1 then
                    begin
                      firstValue := GetIntFieldValue(field);
                      break;
                    end;
              if firstValue <> -1 then
              begin
                firstValue := trunc(firstValue / increment) * increment;
                lastValue := firstValue;
                for i := MovieList.Count-1 downto 0 do
                  with TMovie(MovieList.Items[i]) do
                    if CanInclude(IncOpt) then
                    begin
                      lastValue := GetIntFieldValue(field);
                      Break;
                    end;
                while firstValue <= lastValue do
                begin
                  AddObject(IntToStr(firstValue), Pointer(0));
                  inc(firstValue, increment);
                end; // while
              end; // if <> -1
            end; // if checked
            for i := 0 to MovieList.Count-1 do
              with TMovie(MovieList.Items[i]) do
                if CanInclude(IncOpt) then
                  if GetIntFieldValue(field) > 1 then
                  begin
                    if group then
                      fieldValue := IntToStr(trunc(GetIntFieldValue(field) / increment) * increment)
                    else
                      fieldValue := IntToStr(GetIntFieldValue(field));
                    valueidx := IndexOf(fieldValue);
                    if valueidx > -1 then
                      Objects[valueidx] := pointer(integer(Objects[valueidx]) + 1)
                    else
                    if not group then
                      AddObject(fieldValue, pointer(1));
                  end; // if > 1
            for i := 0 to Count-1 do
            begin
              if group then
                fieldValue := Format('%s -> %d', [Strings[i], StrToInt(Strings[i]) + increment - 1])
              else
                fieldValue := Strings[i];
              TheChart.Series[0].Add(integer(Objects[i]), fieldValue);
            end; // for
          end; // statItemYears, statItemLengths
      end; // case
      TheChart.View3DOptions.Orthogonal := true;
    finally
      Free;
    end; // with TStringList try
  end else
  if idx in statItemsPie then
  begin
    ActionOptionsLabelsExecute(Self);
    ActionOptionsLegendExecute(Self);
    TheChart.Series[1].Clear;
    case idx of
      statItemMediaType     : field := fieldMediaType;
      statItemSource        : field := fieldSource;
      statItemCountries     : field := fieldCountry;
      statItemCategories    : field := fieldCategory;
      statItemVideoFormats  : field := fieldVideoFormat;
      statItemAudioFormats  : field := fieldAudioFormat;
      statItemFramerates    : field := fieldFramerate;
      statItemLanguages     : field := fieldLanguages;
      statItemSubtitles     : field := fieldSubtitles;
      statItemDirectors     : field := fieldDirector;
      statItemProducers     : field := fieldProducer;
      statItemActors        : field := fieldActors;

    end; // case idx

    Groups := MovieList.GetMoviesByGroups(field, IncOpt);
    for i := 0 to Groups.Count-1 do
    begin
      GroupName := Groups.Strings[i];
      GroupMovies := TObjectList(Groups.Objects[i]);
      if GroupName = '$$$EMPTY$$$' then
        GroupName := Messages.Strings[msgNone];
      TheChart.Series[1].YValues.Order := loDescending;
      TheChart.Series[1].Add(GroupMovies.Count, GroupName);
    end;
    FreeObjects(Groups);
    Groups.Free;

    with (TheChart.Series[1] as TPieSeries).OtherSlice do
    begin
      if ActionOptionsGroup.Checked then
      begin
        Style := poBelowPercent;
        Value := 0.5;
        Text := Messages.Strings[msgOthers];
      end
      else
        Style := poNone
    end;
  end else
  begin
    statLength := 0;
    statTotalLength := 0;
    statTotalSize := 0;
    statTotalDiscs := 0;
    if MovieList <> nil then
    begin
      with MovieList do
      begin
        statCount := Count(IncOpt);
        for i := 0 to Count-1 do
        begin
          with TMovie(Items[i]) do
            if CanInclude(IncOpt) then
            begin
              if iLength > 0 then
              begin
                statLength := statLength + iLength;
                Inc(statTotalLength, iLength);
              end
              else
                dec(statCount);
              Inc(statTotalSize, CalcTotalSize);
              if iDisks > 0 then
                Inc(statTotalDiscs, iDisks);
            end;
        end;
        try
          statLength := statLength / statCount;
        except
          statLength := 0;
        end;
        statCount := Count(IncOpt);
      end;
    end else
    begin
      statCount := 0;
    end;
    lstGeneral.Items[0].SubItems[0] := IntToStr(statCount);
    lstGeneral.Items[1].SubItems[0] := Format(Messages.Strings[msgMinutes], [statLength]);
    lstGeneral.Items[2].SubItems[0] := Format(Messages.Strings[msgDayHourMin], [statTotalLength div (60*24), (statTotalLength mod (60*24)) div 60, statTotalLength mod 60]);
    lstGeneral.Items[3].SubItems[0] := IntToStr(statTotalSize);
    lstGeneral.Items[4].SubItems[0] := IntToStr(statTotalDiscs);
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TStatsWin.ActionSaveAsExecute(Sender: TObject);
var
  png: TPNGObject;
  bmp: TBitmap;
begin
  with TSaveDialog.Create(Self) do
    try
      Filter := DialogChartFilter;
      DefaultExt := Copy(extChart[extChWMF], 2, 3);
      Options := DialogSaveOptions;
      InitialDir := Settings.rOptions.rFolders[fuGraphic].Value;
      FileName := '';
      Title := Messages.Strings[msgSave];
      OnTypeChange := Self.OnDialogTypeChange;
      if Execute then
      begin
        Settings.rOptions.rFolders[fuGraphic].Value := ExtractFilePath(FileName);
        case FilterIndex of
          DialogChartFilterWMF:
            TheChart.SaveToMetafile(FileName);
          DialogChartFilterPNG:
            begin
              png := TPNGObject.Create;
              bmp := TBitmap.Create;
              try
                TheChart.SaveToBitmapFile(FileName);
                bmp.LoadFromFile(FileName);
                png.Assign(bmp);
                png.SaveToFile(FileName);
              finally
                png.Free;
                bmp.Free;
              end;
            end;
          DialogChartFilterBMP:
            TheChart.SaveToBitmapFile(FileName);
        end; // case
      end; // if exec
    finally
      Free;
    end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TStatsWin.ActionOptionsLegendExecute(Sender: TObject);
begin
  TheChart.Legend.Visible := ActionOptionsLegend.Checked;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TStatsWin.ActionOptionsLabelsExecute(Sender: TObject);
begin
  TheChart.SeriesList.Series[1].Marks.Visible := ActionOptionsLabels.Checked;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TStatsWin.ActionCopyWMFExecute(Sender: TObject);
begin
  TheChart.CopyToClipboardMetafile(true);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TStatsWin.ActionCopyBMPExecute(Sender: TObject);
begin
  TheChart.CopyToClipboardBitmap;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TStatsWin.ActionCopyExecute(Sender: TObject);
begin
//
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TStatsWin.FormCreate(Sender: TObject);
begin
  ActionCopy.ImageIndex := Ord(ICON_STATSCOPY);
  ActionSaveAs.ImageIndex := Ord(ICON_STATSSAVE);
  ActionOptions.ImageIndex := Ord(ICON_STATSOPTIONS);
  inherited;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TStatsWin.Translate;
begin
  Translator.Translate(IncMovies);
  inherited;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TStatsWin.OnDialogTypeChange(Sender: TObject);
var
  NewName, NewExt: string;
begin
  if Sender is TSaveDialog then
    with Sender as TSaveDialog do
    begin
      if not DirectoryExists(FileName) then
      begin
        case FilterIndex of
          DialogChartFilterWMF:
            begin
              NewName := ChangeFileExt(ExtractFileName(FileName), extChart[extChWMF]);
              NewExt := extChart[extChWMF];
            end;
          DialogChartFilterPNG:
            begin
              NewName := ChangeFileExt(ExtractFileName(FileName), extChart[extChPNG]);
              NewExt := extChart[extChPNG];
            end;
          DialogChartFilterBMP:
            begin
              NewName := ChangeFileExt(ExtractFileName(FileName), extChart[extChBMP]);
              NewExt := extChart[extChBMP];
            end;
        end;
        Delete(NewExt, 1, 1);
        SendMessage(Windows.GetParent(Handle), CDM_SETCONTROLTEXT, 1152, Integer(PChar(NewName)));
        SendMessage(Windows.GetParent(Handle), CDM_SETDEFEXT, 1152, Integer(PChar(NewExt)));
      end;
    end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

end.
