unit EdcProject;

interface

uses
  Classes, SysUtils, Generics.Collections, Generics.Defaults, XML, DateUtils,
  Ean, DOM, XMLRead, XMLWrite, Common, CsvDocument,
  SpotPrice;

type

  { TEdcProject }

  TEdcProject = class
  private
    procedure CreateEan(Number, Owner, Address: string; Kind: TEanKind);
    function FileNameFilter(FileName: string): Boolean;
    procedure LoadEdcReport(FileName: string);
  public
    SpotPrices: TSpotPrices;
    Eans: TEans;
    procedure ImportReports(ReportsDir: string);
    procedure LoadFromFile(FileName: string);
    procedure SaveToFile(FileName: string);
    constructor Create;
    destructor Destroy; override;
  end;


implementation

resourcestring
  SWrongFileFormat = 'Wrong file format';
  STotal = 'Total';

const
  EdcProjectName = 'EdcProject';

function TEdcProject.FileNameFilter(FileName: string): Boolean;
begin
  Result := ExtractFileExt(FileName) = '.csv';
end;

procedure TEdcProject.ImportReports(ReportsDir: string);
var
  Reports: TStringList;
  I: Integer;
begin
  for I := 0 to Eans.Count - 1 do
    Eans[I].Values.Clear;

  Reports := TStringList.Create;
  try
    SearchFiles(Reports, ReportsDir, FileNameFilter);
    for I := 0 to Reports.Count - 1 do
      LoadEdcReport(Reports[I]);
  finally
    Reports.Free;
  end;

  for I := 0 to Eans.Count - 1 do
    Eans[I].Values.Sort(TComparer<TEanValue>.Construct(Eans[I].Values.Comparer));

  CreateEan('', STotal, '', ekSupply);
  CreateEan('', STotal, '', ekConsumption);
end;

procedure TEdcProject.LoadFromFile(FileName: string);
var
  Doc: TXMLDocument;
  RootNode: TDOMNode;
  NewNode: TDOMNode;
begin
  Eans.Clear;
  SpotPrices.Clear;
  ReadXMLFile(Doc, FileName);
  with Doc do
  try
    if Doc.DocumentElement.NodeName <> EdcProjectName then
      raise Exception.Create(SWrongFileFormat);
    RootNode := Doc.DocumentElement;

    NewNode := RootNode.FindNode(EansName);
    if Assigned(NewNode) then
      Eans.LoadFromXmlNode(NewNode);

    NewNode := RootNode.FindNode(SpotPricesName);
    if Assigned(NewNode) then
      SpotPrices.LoadFromXmlNode(NewNode);
  finally
    FreeAndNil(Doc);
  end;
end;

procedure TEdcProject.SaveToFile(FileName: string);
var
  Doc: TXMLDocument;
  RootNode: TDOMNode;
  NewNode: TDOMNode;
begin
  Doc := TXMLDocument.Create;
  with Doc do try
    RootNode := CreateElement(EdcProjectName);
    AppendChild(RootNode);

    if Eans.Count > 0 then begin
      NewNode := RootNode.OwnerDocument.CreateElement(EansName);
      RootNode.AppendChild(NewNode);
      Eans.SaveToXmlNode(NewNode);
    end;

    if SpotPrices.Count > 0 then begin
      NewNode := RootNode.OwnerDocument.CreateElement(SpotPricesName);
      RootNode.AppendChild(NewNode);
      SpotPrices.SaveToXmlNode(NewNode);
    end;

    if ExtractFileDir(FileName) <> '' then
      ForceDirectories(ExtractFileDir(FileName));
    WriteXMLFile(Doc, FileName);
  finally
    FreeAndNil(Doc);
  end;
end;

constructor TEdcProject.Create;
begin
  Eans := TEans.Create;
  SpotPrices := TSpotPrices.Create;
end;

destructor TEdcProject.Destroy;
begin
  FreeAndNil(SpotPrices);
  FreeAndNil(Eans);
  inherited;
end;

procedure TEdcProject.LoadEdcReport(FileName: string);
var
  R: Integer;
  C: Integer;
  CSVDoc: TCSVDocument;
  Date: TDateTime;
  TimeFrom: TDateTime;
  TimeTo: TDateTime;
  CellValueIn: Currency;
  CellValueOut: Currency;
  Ean: TEan;
  EanKind: TEanKind;
  Number: string;
  A: Currency;
begin
  CSVDoc := TCSVDocument.Create;
  try
    CSVDoc.Delimiter := ';';
    CSVDoc.LoadFromFile(FileName);

    for C := 0 to ((CSVDoc.ColCount[0] - 3) div 2) - 1 do begin
      Number := CSVDoc.Cells[3 + C * 2, 0];
      if Copy(Number, 1, 3) = 'IN-' then Number := Copy(Number, 4, MaxInt);
      if Copy(Number, 1, 4) = 'OUT-' then Number := Copy(Number, 5, MaxInt);
      if Copy(Number, Length(Number) - 1, 2) = '-D' then begin
        EanKind := ekSupply;
        Number := Copy(Number, 1, Length(Number) - 2);
      end;
      if Copy(Number, Length(Number) - 1, 2) = '-O' then begin
        EanKind := ekConsumption;
        Number := Copy(Number, 1, Length(Number) - 2);
      end;
      Ean := Eans.SearchByNumber(Number);
      if not Assigned(Ean) then begin
        Ean := TEan.Create;
        Ean.Number := Number;
        Eans.Add(Ean);
      end;
      Ean.Kind := EanKind;

      Ean.Values.Capacity := Ean.Values.Count + CSVDoc.RowCount;

      for R := 1 to CSVDoc.RowCount - 1 do begin
        Date := StrToDate(CSVDoc.Cells[0, R]);
        TimeFrom := Date + StrToTime(CSVDoc.Cells[1, R]);
        TimeTo := Date + StrToTime(CSVDoc.Cells[2, R]);

        CellValueIn := 0;
        if TryStrToCurr(CSVDoc.Cells[3 + C * 2, R], CellValueIn) then begin
          if EanKind = ekConsumption then CellValueIn := -CellValueIn;
        end;

        CellValueOut := 0;
        if TryStrToCurr(CSVDoc.Cells[3 + C * 2 + 1, R], CellValueOut) then begin
          if EanKind = ekConsumption then CellValueOut := -CellValueOut;
        end else CellValueOut := CellValueIn;

        Ean.Values.Add(TEanValue.Create(TimeFrom, CellValueIn, CellValueOut));
      end;
    end;
  finally
    CSVDoc.Free;
  end;
end;

procedure TEdcProject.CreateEan(Number, Owner, Address: string; Kind: TEanKind);
var
  Ean: TEan;
  Values: TDictionary<TDateTime, TEanValue>;
  ValuesArray: TArray<TPair<TDateTime, TEanValue>>;
  I: Integer;
  E: Integer;
  Value: TEanValue;
begin
  Ean := Eans.SearchByOwnerKind(Owner, Kind);
  if not Assigned(Ean) then begin
    Ean := TEan.Create;
    Ean.Number := Number;
    Ean.Owner := Owner;
    Ean.Address := Address;
    Ean.Kind := Kind;
    Eans.Add(Ean);
  end;

  Ean.Values.Clear;

  Values := TDictionary<TDateTime, TEanValue>.Create;
  for E := 0 to Eans.Count - 1 do
  if Eans[E].Kind = Kind then begin
    for I := 0 to Eans[E].Values.Count - 1 do begin
      if Values.TryGetValue(Eans[E].Values[I].Time, Value) then begin
        Value.ValueIn := Value.ValueIn + Eans[E].Values[I].ValueIn;
        Value.ValueOut := Value.ValueOut + Eans[E].Values[I].ValueOut;
        Values[Eans[E].Values[I].Time] := Value;
      end else begin
        Values.Add(Eans[E].Values[I].Time, TEanValue.Create(0, Eans[E].Values[I].ValueIn, Eans[E].Values[I].ValueOut));
      end;
    end;
  end;

  ValuesArray := Values.ToArray;
  Ean.Values.Capacity := Values.Count;
  for I := 0 to Values.Count - 1 do begin
    Ean.Values.Add(TEanValue.Create(ValuesArray[I].Key, ValuesArray[I].Value.ValueIn, ValuesArray[I].Value.ValueOut));
  end;
  Ean.Values.Sort(TComparer<TEanValue>.Construct(Ean.Values.Comparer));

  Values.Free;
end;

end.

