unit uEvaluadorContratosMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, uAuxiliares, xMatDefs, uconstantes, uCrearBinDatosHorarios,
  ExtCtrls, uBaseAltasEditores, uDatosHorariosDetallados, Grids, utilidades, utrazosxy;

type
  TEvaluadorContratos = class(TBaseAltasEditores)
    ENArchSim: TEdit;
    LArchSim: TLabel;
    BBuscarSim: TButton;
    BEvaluar: TButton;
    BSalir: TButton;
    OpenDialogDem: TOpenDialog;
    OpenDialogSim: TOpenDialog;
    LNodo: TLabel;
    CBNodo: TComboBox;
    GBDatosContrato: TGroupBox;
    PContrato: TPanel;
    LPrecioP: TLabel;
    LPrecioE: TLabel;
    LArchDems: TLabel;
    EPrecioFijo: TEdit;
    EPrecioE: TEdit;
    ENArchDem: TEdit;
    BBuscarDems: TButton;
    BCrear: TButton;
    GBResultados: TGroupBox;
    PResultados: TPanel;
    LIngresosFijos: TLabel;
    LIngresosVariables: TLabel;
    LIngresosTotales: TLabel;
    EIngresosFijos: TEdit;
    EIngresosVariables: TEdit;
    EIngresosTotales: TEdit;
    SGInsumos: TStringGrid;
    procedure BBuscarSimClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure BSalirClick(Sender: TObject);
    procedure BCrearClick(Sender: TObject);
    procedure BEvaluarClick(Sender: TObject);
    procedure CBNodoChange(Sender: TObject);
    procedure BBuscarDemsClick(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  private
    encabActores: String;
    finiSim, ffinSim: TDateTime;
    horasDelContrato: Integer;
    nCronicas, nPasos, nPostes: Integer;
    durPos: TDAofNReal;
    durPaso: Integer;
    datosSimOK : boolean;
    PrecioFijo, PrecioE: NReal;

    fIniDem, fFinDem: TDateTime;
    IngresosFijos : NReal;     //[USD] IngresosFijos= PrecioFijo * cantHoras
    IngresosVariables : NReal; //[USD] IngresosVariables= suma_h(Ph * 1 hora * PrecioE)
    IngresosTotales: NReal;    //[USD] Es lo que gana el generador por aceptar el contrato
                               //Ingresos:= IngresosFijos + IngresosVariables

    InsumosCmgMayorPE : TDAofNReal; //[USD] InsumosCmgMayorPE= suma_h(Ph * 1 hora * PE * (Cmg > PrecioE))
    InsumosCmgMenorPE : TDAofNReal; //[USD] InsumosCmgMayorPE= suma_h(Ph * 1 hora * PE * (Cmg <= PrecioE))
    InsumosTotales : TDAofNReal;    //[USD] InsumosTotales= InsumosCmgMayorPE + InsumosCmgMayorPE

    columnasDelNodo: TDAofNInt;

    stop: boolean;        //indica si se quiere cerrar la ventana mientras se esta procesando
    procesando: boolean;  //indica si se ha comenzado a procesar un archivo de simulación

    procedure posicionarseEnPrimeraCronica(var f: TextFile);
    procedure cargarDatosSim;
    procedure hallarColumnasDelNodo;
    procedure calcularIngresos(f : TDatosHorariosDetallados);
    procedure procesarCronica(f : TDatosHorariosDetallados ; var sim : TextFile ; numeroCronica: Integer);
    procedure evaluar;
    procedure mostrarGrafico;
  protected
    function validarNombreNodo : boolean;
    function validarFormulario : boolean; override;
  public
    { Public declarations }
  end;

var
  EvaluadorContratos: TEvaluadorContratos;

implementation

uses Math;

{$R *.dfm}

procedure TEvaluadorContratos.posicionarseEnPrimeraCronica(var f: TextFile);
var
  linea: String;
  i: Integer;
begin
  for i:= 1 to 8 do
    readln(f, linea);
end;

procedure TEvaluadorContratos.cargarDatosSim;
var
  linea: String;
  pal: String;
  i, k: Integer;
  f: TextFile;
begin
  try
    AssignFile(f, ENArchSim.Text);
    reset(f);
    //Inicio Simulación
    readln(f, linea);
    //FechaIni, FechaFin
    readln(f, linea);
    nextPal(linea);
    pal:= NextPal(linea);
    finiSim:= StrToDate(pal);
    NextPal(linea);
    pal:= NextPal(linea);
    ffinSim:= StrToDate(pal);
    //NActores
    readln(f, linea);
    //NCronicas
    readln(f, linea);
    NextPal(linea);
    nCronicas:= NextInt(linea);
    //NPasos
    readln(f, linea);
    NextPal(linea);
    nPasos:= NextInt(linea);
    //NPostes
    readln(f, linea);
    NextPal(linea);
    nPostes:= NextInt(linea);
    if durPos <> NIL then SetLength(durPos, 0);
    SetLength(durPos, nPostes);
    //Durpos
    readln(f, linea);
    NextPal(linea);
    for i:= 0 to nPostes -1 do
      durPos[i]:= NextInt(linea);
    durPaso:= round(vsum(durPos));
    readln(f, linea);
    //CRONICA: ... RandSeed:
    readln(f, linea);
    readln(f, encabActores);
    linea:= copy(encabActores, 0, MAXINT);
    CBNodo.Clear;
    repeat
      i:= pos(#9, linea);
      pal:= copy(linea, 0, i-1);
      linea:= copy(linea, i + 1, MAXINT);
      k:= Pos('<Nodo> ', pal);
      if (k <> 0) and (CBNodo.Items.IndexOf(pal) = -1) then
        CBNodo.Items.Add(pal);
    until i = 0;
    SGInsumos.ColCount:= nCronicas + 1;
    for i:= 1 to SGInsumos.ColCount -1 do
      SGInsumos.Cells[i, 0]:= IntToStr(i);
    utilidades.AutoSizeTable(GBResultados, SGInsumos, GBResultados.ClientWidth, GBResultados.ClientWidth);
    datosSimOK:= true;
  finally
    CloseFile(f);
  end;
end;

procedure TEvaluadorContratos.hallarColumnasDelNodo;
var
  nombreNodo, aux, pal: String;
  k, i, columna: integer;
begin
  if CBNodo.ItemIndex <> -1 then
  begin
    SetLength(columnasDelNodo, 0);
    SetLength(columnasDelNodo, nPostes);
    nombreNodo:= CBNodo.Items[CBNodo.ItemIndex];
    aux:= copy(encabActores, 0, MaxInt);
    k:= 0;
    columna:= 1;
    repeat
      //parseo las palabras del encabezado
      i:= pos(#9, aux);
      pal:= copy(aux, 0, i-1);
      aux:= copy(aux, i + 1, MAXINT);

      if pal = nombreNodo then
      begin
        columnasDelNodo[k]:= columna;
        inc(k);
      end;
      inc(columna);
    until i = 0;
  end;
end;

procedure TEvaluadorContratos.calcularIngresos(f : TDatosHorariosDetallados);
var
  i, j: Integer;
  P: TDAofNReal;
begin
  IngresosFijos:= horasDelContrato * PrecioFijo;
  SetLength(P, 64);
  i:= 0;
  IngresosVariables:= 0;
  while i < horasDelContrato div 64 do
  begin
    f.ReadBuff(P, fIniDem + 64 * i / 24);
    for j:= 0 to high(P) do
      IngresosVariables:= IngresosVariables + P[j] * PrecioE;
    inc(i);
  end;
  SetLength(P, 0);
  SetLength(P, horasDelContrato - (i * 64));
  f.ReadBuff(P, fIniDem + 64 * i / 24);
  for j:= 0 to high(P) do
    IngresosVariables:= IngresosVariables + P[j] * PrecioE;
  IngresosTotales:= IngresosFijos + IngresosVariables;

  EIngresosFijos.Text:= copy(FloatToStrF(IngresosFijos/1000, ffCurrency, 10, 2), 3, MAXINT);
  EIngresosVariables.Text:= copy(FloatToStrF(IngresosVariables/1000, ffCurrency, 10, 2), 3, MAXINT);
  EIngresosTotales.Text:= copy(FloatToStrF(IngresosTotales/1000, ffCurrency, 10, 2), 3, MAXINT);
  Application.ProcessMessages;
end;

procedure TEvaluadorContratos.procesarCronica(f : TDatosHorariosDetallados ; var sim : TextFile ; numeroCronica: Integer);
var
  linea : String;
  iPaso, iCol, iPoste, hora, jhora: Integer;
  cmgsPorPoste: TDAofNReal;
  fechaFila: TDateTime;
  PHoraria, PPoste: TDAofNReal;
  EnergiaDelPoste, insumosDelPoste: NReal;
  nHorasDelPoste: Integer;
begin
  //CRONICA
  readln(sim, linea);
  //Actores
  readln(sim, linea);
  //Variables
  readln(sim, linea);
  SetLength(cmgsPorPoste, nPostes);
  SetLength(PPoste, nPostes);
  SetLength(PHoraria, durPaso);

  InsumosCmgMayorPE[numeroCronica]:= 0;
  InsumosCmgMenorPE[numeroCronica]:= 0;
  InsumosTotales[numeroCronica]:= 0;
  for iPaso:= 0 to nPasos -1 do
  begin
    readln(sim, linea);
    fechaFila:= finiSim + iPaso * durPaso / 24;
    if (fechaFila >= fIniDem) and (fechaFila < fFinDem) then
    begin
      //Hallo los costos marginales del nodo en los distintos postes para el paso
      iCol:= 1;
      for iPoste:= 0 to nPostes -1 do
      begin
        while iCol < columnasDelNodo[iPoste] do
        begin
          NextPal(linea);
          inc(iCol);
        end;
        cmgsPorPoste[iPoste]:= NextFloat(linea);
        inc(iCol);
      end;

      //Calculo las potencias por poste para el paso
      f.ReadBuff(PHoraria, fechaFila);
      QuickSort_Decreciente(PHoraria);
      hora:= 0;
      for iposte:= 0 to nPostes -1 do
      begin
        nHorasDelPoste:= round(durPos[iposte]);
        EnergiaDelPoste:= 0;
        for jhora:= 0 to nHorasDelPoste-1 do
        begin
          EnergiaDelPoste:= EnergiaDelPoste + PHoraria[hora];
          hora:= hora+1;
        end;
        PPoste[iposte]:= EnergiaDelPoste / nHorasDelPoste;
      end;

      //Calculo los insumos para el paso de la crónica
      for iPoste:= 0 to nPostes -1 do
      begin
        insumosDelPoste:= cmgsPorPoste[iPoste] * durPos[iPoste] * PPoste[iPoste];
        if cmgsPorPoste[iPoste] > PrecioE then
          InsumosCmgMayorPE[numeroCronica]:= InsumosCmgMayorPE[numeroCronica] + insumosDelPoste
        else
          InsumosCmgMenorPE[numeroCronica]:= InsumosCmgMenorPE[numeroCronica] + insumosDelPoste;
        InsumosTotales[numeroCronica]:= InsumosTotales[numeroCronica] + insumosDelPoste;
      end;
    end
  end;
  //''
  readln(sim, linea);
end;

procedure TEvaluadorContratos.evaluar;
var
  f: TDatosHorariosDetallados;
  sim: TextFile;
  iCron: Integer;
begin
  procesando:= true;
  f:= TDatosHorariosDetallados.Create(ENArchDem.Text);
  fIniDem:= f.fechaPrimerDia;
  fFinDem:= f.fechaUltimoDia;
  horasDelContrato:= trunc((fFinDem - fIniDem) * 24);

  calcularIngresos(f);

  SetLength(InsumosCmgMayorPE, nCronicas);
  SetLength(InsumosCmgMenorPE, nCronicas);
  SetLength(InsumosTotales, nCronicas);

  AssignFile(sim, ENArchSim.Text);
  reset(sim);
  posicionarseEnPrimeraCronica(sim);

  for iCron:= 0 to nCronicas -1 do
  begin
    procesarCronica(f, sim, iCron);
    SGInsumos.Cells[iCron + 1, 1]:= copy(FloatToStrF(InsumosCmgMayorPE[iCron]/1000, ffCurrency, 10, 2), 3, MAXINT);
    SGInsumos.Cells[iCron + 1, 2]:= copy(FloatToStrF(InsumosCmgMenorPE[iCron]/1000, ffCurrency, 10, 2), 3, MAXINT);
    SGInsumos.Cells[iCron + 1, 3]:= copy(FloatToStrF(InsumosTotales[iCron]/1000, ffCurrency, 10, 2), 3, MAXINT);
    utilidades.AutoSizeCol(SGInsumos, iCron + 1);
    utilidades.AutosizeTable(GBResultados, SGInsumos, GBResultados.ClientWidth, GBResultados.ClientHeight);
    SGInsumos.Col:= iCron + 1;
    Application.ProcessMessages;
    if stop then
      break;
  end;
  procesando:= false;
  if stop then
    self.CloseQuery;
end;

procedure TEvaluadorContratos.mostrarGrafico;
var
  ventanaGrafico: TfrmDllForm;
  maxX: NReal;
  i: Integer;
  pasoX: NReal;
begin
  QuickSort_Decreciente(InsumosTotales);
  pasoX:= 100/nCronicas;  
  maxX:= Max(InsumosTotales[0] * 1.01/1000, IngresosTotales * 1.01 /1000);
  ventanaGrafico:= TfrmDllForm.Create(self);
  ventanaGrafico.CrearDiagramaXY('Evaluación del Contrato',
                                 nCronicas, false,
                                 '%', 'Insumos Totales[kUSD]',
                                 clBlue, pasoX, 100, 0, maxX, 10, 10);
  ventanaGrafico.CrearSerieXY('Ingresos Totales[kUSD]', nCronicas, false, clRed);

 	ventanaGrafico.xlabel('%');
	ventanaGrafico.ylabel('[kUSD]');
	ventanaGrafico.Etiquetas_x(pasoX, 100);
	ventanaGrafico.Etiquetas_y(0, maxX);
	ventanaGrafico.titulo('Evaluación del Contrato');

	ventanaGrafico.Show;

	ventanaGrafico.dbj_xlabel;
	ventanaGrafico.dbj_ylabel;
	ventanaGrafico.dbj_titulo;
	ventanaGrafico.dbj_etiquetasx;
	ventanaGrafico.dbj_etiquetasy;
	ventanaGrafico.dbj_borde;

  ventanaGrafico.BringToFront;
  
  pasoX:= 100/nCronicas;  
  for i:= 0 to nCronicas -1 do
  begin
    ventanaGrafico.tr1.PlotNuevo_x(pasoX * (i + 1));
    ventanaGrafico.tr1.PlotNuevo_y(1, InsumosTotales[i]/1000);
    ventanaGrafico.tr1.PlotNuevo_y(2, IngresosTotales/1000);
  end;
end;

function TEvaluadorContratos.validarNombreNodo : boolean;
begin
  if CBNodo.ItemIndex <> -1 then
    result:= true
  else
  begin
    ShowMessage('Debe seleccionar un nodo de la lista');
    result:= False;
  end;
end;

function TEvaluadorContratos.validarFormulario : boolean;
begin
  result:= inherited validarFormulario and
           validarNombreNodo and
           validarEditNarch(ENArchSim) and
           validarEditNarch(ENArchDem) and
           validarEditFloat(EPrecioFijo, 0, MaxNReal) and
           validarEditFloat(EPrecioE, 0, MaxNReal) and
           FileExists(ENArchDem.Text);
end;

procedure TEvaluadorContratos.BBuscarSimClick(Sender: TObject);
begin
  if OpenDialogSim.Execute then
  begin
    ENArchSim.Text:= OpenDialogSim.FileName;
    try
      cargarDatosSim;
    Except
      ShowMessage('ERROR: No se pudo leer el archivo de simulación!!');
    end;
  end;
end;

procedure TEvaluadorContratos.FormCreate(Sender: TObject);
var
  i: Integer;
begin
  OpenDialogSim.InitialDir:= uconstantes.getDir_Run;
  OpenDialogDem.InitialDir:= uconstantes.getDir_DatosComunes;
  datosSimOK:= false;
  guardado:= true;
  self.Left:= 33;
  self.Top:= 13;
  BEvaluar.Left:= self.ClientWidth div 4 - BEvaluar.Width div 2;
  BSalir.Left:= (Self.ClientWidth * 3) div 4 - BSalir.Width div 2;
  SGInsumos.Cells[0, 0]:= 'Crónica:';
  SGInsumos.Cells[0, 1]:= 'Insumos Cmg > PE[kUSD]:';
  SGInsumos.Cells[0, 2]:= 'Insumos Cmg <= PE[kUSD]:';
  SGInsumos.Cells[0, 3]:= 'Insumos Totales[kUSD]';
  utilidades.AutoSizeCol(SGInsumos, 0);
  for i:= 1 to SGInsumos.ColCount -1 do
    SGInsumos.Cells[i, 0]:= IntToStr(i);
  utilidades.AutoSizeTable(GBResultados, SGInsumos, GBResultados.ClientWidth, GBResultados.ClientWidth);
end;

procedure TEvaluadorContratos.BSalirClick(Sender: TObject);
begin
  guardado:= true;
  self.Close;
end;

procedure TEvaluadorContratos.BCrearClick(Sender: TObject);
var
  form: TCrearBinDatosHorarios;
begin
  form:= TCrearBinDatosHorarios.Create(self);
  if form.ShowModal = mrOk then
  begin
    Self.ENArchDem.Text:= form.darNombreArch;
  end;
  form.Free;
end;

procedure TEvaluadorContratos.BEvaluarClick(Sender: TObject);
begin
  if validarFormulario then
  begin
    PrecioFijo:= StrToFloat(EPrecioFijo.Text);
    PrecioE:= StrToFloat(EPrecioE.Text);
    evaluar;
    mostrarGrafico;
  end;
end;

procedure TEvaluadorContratos.CBNodoChange(Sender: TObject);
begin
  hallarColumnasDelNodo;
end;

procedure TEvaluadorContratos.BBuscarDemsClick(Sender: TObject);
begin
  if OpenDialogDem.Execute then
    ENArchDem.Text:= OpenDialogDem.FileName;
end;

procedure TEvaluadorContratos.FormCloseQuery(Sender: TObject;
  var CanClose: Boolean);
begin
  guardado:= true;
  if not procesando then
    inherited FormCloseQuery(Sender, CanClose)
  else
    stop:= true;
end;

end.
