unit uEvaluadorContratos;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, uAuxiliares, xMatDefs, uconstantesSimSEE, uCrearBinDatosHorarios,
  ExtCtrls, uBaseAltasEditores, uDatosHorariosDetallados, Grids, utilidades, utrazosxy, ufechas,
  uSeleccionarArchivo, uDTResultados, uverdoc, unodos;

type
  TEvaluadorContratos = class(TBaseAltasEditores)
    BEvaluar: TButton;
    BSalir: TButton;
    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;
    LIniSim: TLabel;
    EFiniSim: TEdit;
    EFFinSim: TEdit;
    LFinSim: TLabel;
    BAyuda: TButton;
    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);
    procedure BAyudaClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    archiSim: String;
    datosSim: DTResultadoSim;
    horasDelContrato: 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

    descsActores: TList {of TResumenActor};
//    primerPasoUtil, ultimoPasoUtil: Integer;

    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
    Constructor Create(Owner : TComponent ; archiSim: String); reintroduce;
    class function filtrosDeArchivos: String;
    class function filtrosDeArchivosBin: String;
  end;

var
  EvaluadorContratos: TEvaluadorContratos;

implementation

uses Math, uCosa;

{$R *.dfm}

Constructor TEvaluadorContratos.Create(Owner : TComponent ; archiSim: String);
begin
  inherited Create(Owner);
  self.archiSim:= archiSim;
  self.Caption:= 'Evaluador de Contratos - ' + archiSim;
  descsActores:= TList.Create;
end;

class function TEvaluadorContratos.filtrosDeArchivos: String;
begin
  result:= 'Archivos de Resultado de Simulación (simres_*.xlt)|simres_*.xlt|Todos los Archivos (*.*)|*.*';
end;

class function TEvaluadorContratos.filtrosDeArchivosBin: String;
begin
  result:= 'Archivos Binarios de Números Reales (*.bin)|*.bin|Todos los Archivos (*.*)|*.*';
end;

procedure TEvaluadorContratos.cargarDatosSim;
var
  i: Integer;
  descActor: TResumenActor;
begin
  datosSim:= DTResultadoSim.Create(archiSim);
  datosSim.read_DatosSim;

  EFiniSim.Text:= DateTimeToStr(datosSim.fechaIni);
  EFFinSim.Text:= DateTimeToStr(datosSim.fechaFin);
  for i:= 0 to high(datosSim.resumenActores) do
  begin
    descActor:= datosSim.resumenActores[i];
    if descActor.Clase.InheritsFrom(TNodo) then
      CBNodo.Items.Add(descActor.nombre);
  end;

  SGInsumos.ColCount:= datosSim.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;
end;

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

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

procedure TEvaluadorContratos.calcularIngresos(f : TDatosHorariosDetallados);
const
  TamBufferDeDatos = 512;
var
  i, j: Integer;
  P: TDAofNReal;
begin
  IngresosFijos:= horasDelContrato * PrecioFijo;
  SetLength(P, TamBufferDeDatos); //Leo de a 8 * TamBufferDeDatos = 4096 bytes
  i:= 0;
  IngresosVariables:= 0;
  //Voy leyendo de a TamBufferDeDatos
  while i < horasDelContrato div TamBufferDeDatos do
  begin
    f.ReadBuff(P, fIniDem + (TamBufferDeDatos / 24) * i);
    for j:= 0 to high(P) do
      IngresosVariables:= IngresosVariables + P[j] * PrecioE;
    inc(i);
  end;
  SetLength(P, 0);
  //Leo los horasDelContrato mod TamBufferDeDatos valores que me faltan
  SetLength(P, horasDelContrato - (i * TamBufferDeDatos));
  f.ReadBuff(P, fIniDem + (TamBufferDeDatos / 24) * i);
  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;
  durPasoEnDias: NReal;
begin
  //CRONICA
  readln(sim, linea);
  //Actores
  readln(sim, linea);
  //Unidades
  readln(sim, linea);
  //Variables
  readln(sim, linea);
  SetLength(cmgsPorPoste, datosSim.nPostes);
  SetLength(PPoste, datosSim.nPostes);
  SetLength(PHoraria, datosSim.durPaso);

  InsumosCmgMayorPE[numeroCronica]:= 0;
  InsumosCmgMenorPE[numeroCronica]:= 0;
  InsumosTotales[numeroCronica]:= 0;

  durPasoEnDias:= datosSim.durPaso / 24;
  for iPaso:= 0 to datosSim.nPasos -1 do
  begin
    readln(sim, linea);
    fechaFila:= datosSim.fechaIni + iPaso * durPasoEnDias;
    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 datosSim.nPostes -1 do
      begin
        while iCol < columnasDelNodo[iPoste] do
        begin
          nextTab(linea);
          inc(iCol);
        end;
        cmgsPorPoste[iPoste]:= StrToFloat(nextTab(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 datosSim.nPostes -1 do
      begin
        nHorasDelPoste:= round(datosSim.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 datosSim.nPostes -1 do
      begin
        insumosDelPoste:= cmgsPorPoste[iPoste] * datosSim.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
  unMilesimo : NReal;
  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, datosSim.nCronicas);
  SetLength(InsumosCmgMenorPE, datosSim.nCronicas);
  SetLength(InsumosTotales, datosSim.nCronicas);

  AssignFile(sim, archiSim);
  reset(sim);
  datosSim.pararseEnCronicaI(1, sim);

  unMilesimo:= 1/1000;
  for iCron:= 0 to datosSim.nCronicas -1 do
  begin
    procesarCronica(f, sim, iCron);
    SGInsumos.Cells[iCron + 1, 1]:= copy(FloatToStrF(InsumosCmgMayorPE[iCron]*unMilesimo, ffCurrency, 10, 2), 3, MAXINT);
    SGInsumos.Cells[iCron + 1, 2]:= copy(FloatToStrF(InsumosCmgMenorPE[iCron]*unMilesimo, ffCurrency, 10, 2), 3, MAXINT);
    SGInsumos.Cells[iCron + 1, 3]:= copy(FloatToStrF(InsumosTotales[iCron]*unMilesimo, 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/ datosSim.nCronicas;
  maxX:= Max(InsumosTotales[0] * 1.01/1000, IngresosTotales * 1.01 /1000);
  ventanaGrafico:= TfrmDllForm.Create(self);
  ventanaGrafico.CrearDiagramaXY('Evaluación del Contrato',
                                 datosSim.nCronicas, false,
                                 '%', 'Insumos Totales[kUSD]',
                                 clBlue, pasoX, 100, 0, maxX, 10, 10);
  ventanaGrafico.CrearSerieXY('Ingresos Totales[kUSD]', datosSim.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/datosSim.nCronicas;
  for i:= 0 to datosSim.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(ENArchDem) and
           validarEditFloat(EPrecioFijo, 0, MaxNReal) and
           validarEditFloat(EPrecioE, 0, MaxNReal) and
           FileExists(ENArchDem.Text);
end;

procedure TEvaluadorContratos.FormCreate(Sender: TObject);
var
  i: Integer;
begin
  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);
  cargarDatosSim;
end;

procedure TEvaluadorContratos.FormDestroy(Sender: TObject);
var
  i: Integer;
begin
  for i:= 0 to descsActores.Count - 1 do
    TResumenActor(descsActores[i]).free;
  descsActores.Free;
  inherited;
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, datosSim.fechaIni, datosSim.fechaFin);
  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.BAyudaClick(Sender: TObject);
begin
  verdoc( 'procesador-resultados-EvaluadorContratos', 'Ayuda del Evaluador de Contratos');
end;

procedure TEvaluadorContratos.BBuscarDemsClick(Sender: TObject);
var
  fSelect: TSeleccionarArchivo;
begin
  fSelect:= TSeleccionarArchivo.Create(self, TEvaluadorContratos.filtrosDeArchivosBin, uconstantesSimSEE.getDir_DatosComunes, false);
  if fSelect.ShowModal = mrOk then
  begin
    ENArchDem.Text:= fSelect.getFileName;
    fSelect.Free;
  end
  else
  begin
    fSelect.Free;
  end;
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.
