unit uResultadoOpt;

{$MODE Delphi}

interface

uses
  xMatDefs, Classes, SysUtils, uAuxiliares, uFechas, uParseSimRes, FileUtil;

type
  TDescVar = class
    public
      nombre, nombreTraduccion: String;
      nDiscs:Integer;
      unidades, unidadesTraduccion: String;
      discretizaciones, discretizacionesTraduccion: TDAOfNReal;

      //El estado seleccionado para la variable si esta fija
      pos: Integer;
      //Cantidad de columnas que hay que avanzar para incrementar el estado de la variable
      incEstadoCada: Integer;

      constructor Create(nombre, nombreTraduccion, unidades, unidadesTraduccion: String; discretizaciones, discretizacionesTraduccion: TDAofNReal; incEstadoCada: Integer);
  end;

  TResultadoOpt = class
    private
      hayDiscretas, hayContinuas: boolean;

      procedure leerEncabezados(var f: TextFile);
      procedure leerDatos(var f: TextFile); overload;
    public
      archiOptRes: String;

      fActPaso: NReal;
      nVarsDiscretas, nVarsContinuas, nEstrellasPorPuntoT, nPasos: Integer;
      descVars: TList {of TDescVar};

      //Es una matriz con nPasos filas y nPuntosT + 1 columnas
      //En cada fila tiene en la posicion 0 la fecha del paso y en el resto los
      //datos del archivo
      datos: TMatOfNReal;

      //El constructor crea la clase y lee los encabezados del archivo, si leerDatos
      //es true tambien se lee la matriz de resultados. Sino habrá que leerla con
      //leerDatos()
      Constructor Create(optRes: String; leerMatrizDatos: boolean);
      procedure leerDatos; overload;
      procedure Free;

      procedure setPos(const nombreVar : String; pos: Integer);
      function getPos(const nombreVar : String): Integer;

      //Retorna las filas final e inicial de los datos para el rango de fechas seleccionado
      procedure recalcRangoFechas(const fechaIni, fechaFin: TDateTime; var filaIni, filaFin: Integer);
      //Retorna en cols las columnas de los datos que contienen los valores de la
      //variable libre para los valores dados de las variables fijas
      //Las variables avanzan 1 lugar en su estado cada la productoria del numero
      //discretizaciones de las variables que se encuentren antes de ella en descVars
      function recalcCols(const nomVarLibre: String): TDAofNInt;

      function getDescVar(const nombreVar : String): TDescVar;
      function indiceDescVar(const nombreVar : String): Integer;
      //Retorna el minimo y maximo del rango seleccionado
      procedure minMaxDatos(filaIni, filaFin : Integer; cols: TDAofNInt; var Min, Max : NReal);

      procedure exportarAxlt(const nomVarLibre: String; const filaIni, filaFin: Integer; const cols: TDAofNInt); overload;
      procedure exportarAxlt(const nomVarLibre: String; const fechaIni, fechaFin: TDateTime); overload;

      function hallarX(nivel: NReal; const ys: TDAofNReal; colIniYs, colFinYs: Integer; const xs: TDAofNReal): NReal;
      procedure compararVariableContraCortes(const nombreVar: String; const cortes: TDAofNReal; const filaIni, filaFin: Integer; archivoSalida: String); overload;
      procedure compararVariableContraCortes(const nombreVar: String; const cortes: TDAofNReal; const fechaIni, fechaFin: TDateTime; archivoSalida: String); overload;
  end;

implementation

constructor TDescVar.Create(nombre, nombreTraduccion, unidades, unidadesTraduccion: String; discretizaciones, discretizacionesTraduccion: TDAofNReal; incEstadoCada: Integer);
begin
  inherited Create();
  Self.nombre:= nombre;
  Self.nombreTraduccion:= nombreTraduccion;
  self.unidades:= unidades;
  self.unidadesTraduccion:= unidadesTraduccion;
  self.discretizaciones:= discretizaciones;
  self.discretizacionesTraduccion:= discretizacionesTraduccion;
  self.nDiscs:= length(discretizaciones);
  self.pos:= 0;
  self.incEstadoCada:= incEstadoCada;
end;

Constructor TResultadoOpt.Create(optRes: String; leerMatrizDatos: boolean);
var
  f: TextFile;
begin
  if FileExistsUTF8(optRes) { *Converted from FileExists*  } then
  begin
    inherited Create;
    archiOptRes:= optRes;
    AssignFile(f, optRes);
    try
      Reset(f);
      leerEncabezados(f);
      if leerMatrizDatos then
        leerDatos(f)
      else
        datos:= NIL;
      CloseFile(f);
    Except
      CloseFile(f);
      raise;
    end;
  end
  else
    raise Exception.Create('TLectorArchivosOptRes.Create: no se encuentra el archivo ' + optRes);
end;

procedure TResultadoOpt.leerDatos;
var
  f: TextFile;
  i, nLineasALeer: Integer;
  linea: String;
begin
  AssignFile(f, archiOptRes);
  try
    Reset(f);

    //fActPaso + nContinuas + nDiscretas + nEstrellas/PuntoT + nPuntosT
    nLineasALeer:= 5;
    //"-- Descripción variables contínuas --"
    if hayContinuas then
      nLineasALeer:= nLineasALeer + 1;
    //"-- Descripción variables contínuas --"
    if hayDiscretas then
      nLineasALeer:= nLineasALeer + 1;
    for i:= 0 to descVars.Count - 1 do
      if TDescVar(descVars[i]).discretizacionesTraduccion <> NIL then
        nLineasALeer:= nLineasALeer + 7
      else
        nLineasALeer:= nLineasALeer + 6;

    //linea en blanco + encabezados
    nLineasALeer:= nLineasALeer + 2;

    for i:= 1 to nLineasALeer do
      readln(f, linea);

    leerDatos(f);
    CloseFile(f);
  Except
    CloseFile(f);
    raise;
  end;
end;

procedure TResultadoOpt.Free;
var
  I: Integer;
begin
  for i:= 0 to descVars.Count - 1 do
    TDescVar(descVars[i]).Free;
  descVars.Free;

  if datos <> NIL then
    liberarMatriz(datos);

  inherited Free;
end;

procedure TResultadoOpt.setPos(const nombreVar : String; pos: Integer);
begin
  getDescVar(nombreVar).pos:= pos;
end;

function TResultadoOpt.getPos(const nombreVar : String): Integer;
begin
  result:= getDescVar(nombreVar).pos;
end;

procedure TResultadoOpt.recalcRangoFechas(const fechaIni, fechaFin: TDateTime ; var filaIni, filaFin: Integer);
begin
  if (fechaIni < datos[0][0]) or (fechaFin > datos[nPasos -1][0]) then
    raise Exception.Create('El rango de fechas seleccionado esta fuera de los datos provistos.')
  else if fechaIni > fechaFin then
    raise Exception.Create('La fecha de inicio es posterior a la fecha de fin!');

  filaIni:= 0;
  while datos[filaIni][0] < fechaIni do
    inc(filaIni);
  filaFin:= filaIni;
  while datos[filaFin][0] < fechaFin do
    inc(filaFin);
end;

function TResultadoOpt.recalcCols(const nomVarLibre: String): TDAofNInt;
var
  iVarLibre, i: Integer;
  base: Integer;
  res: TDAofNInt;
begin
  iVarLibre:= indiceDescVar(nomVarLibre);

  SetLength(res, TDescVar(descVars[iVarLibre]).nDiscs);
  base:= 1;
  for i:= 0 to iVarLibre - 1 do
    base:= base + TDescVar(descVars[i]).pos * TDescVar(descVars[i]).incEstadoCada;

  for i:= iVarLibre + 1 to descVars.Count -1 do
    base:= base + TDescVar(descVars[i]).pos * TDescVar(descVars[i]).incEstadoCada;

  for i:= 0 to high(res) do
    res[i]:= base + i * TDescVar(descVars[iVarLibre]).incEstadoCada;
  result:= res;
end;

function TResultadoOpt.getDescVar(const nombreVar : String): TDescVar;
var
  i: Integer;
  res: TDescVar;
begin
  res:= NIL;
  for i:= 0 to descVars.Count - 1 do
    if nombreVar = TDescVar(descVars[i]).nombre then
    begin
      res:= descVars[i];
      break;
    end;
  result:= res;
end;

//Las variables avanzan 1 lugar en su estado cada la productoria del numero
//discretizaciones de las variables que se encuentren antes de ella en descVars
function TResultadoOpt.indiceDescVar(const nombreVar : String) : Integer;
var
  i, res : Integer;
begin
  res:= -1;
  for i:= 0 to descVars.Count - 1 do
    if nombreVar = TDescVar(descVars[i]).nombre then
    begin
      res:= i;
      break;
    end;
  result:= res;
end;

procedure TResultadoOpt.minMaxDatos(filaIni, filaFin : Integer; cols: TDAofNInt; var Min, Max : NReal);
var
  iFila, iColumna: Integer;
  val: NReal;
  fila: TDAofNReal;
begin
  min:= MaxNReal; max := -MaxNReal;
  for iFila:= filaIni to filaFin do
  begin
    fila:= datos[iFila];
    for iColumna:= 0 to high(cols) do
    begin
      val:= fila[cols[iColumna]];
      if val < min then
        min:= val;
      if val > max then
        max:= val;
    end;
  end;
end;

procedure TResultadoOpt.exportarAxlt(const nomVarLibre: String; const filaIni, filaFin: Integer; const cols: TDAofNInt);
var
  arch: TextFile;
  linea: String;
  i, j: Integer;
  descVarLibre: TDescVar;
  fila: TDAofNReal;
begin
  try
    AssignFile(arch, 'excel.xlt');
    Rewrite(arch);


//    SGDatos.RowCount:= filaFin - filaIni + 2;
//    SGDatos.ColCount:= length(cols) + 1;

    descVarLibre:= descVars[indiceDescVar(nomVarLibre)];
    linea:= 'Fecha\Estado[' + descVarLibre.unidades + ']' + #9;
    for j := 0 to high(cols) do
      linea:= linea + FloatToStrF(descVarLibre.discretizaciones[j], ffFixed, 10, 2) + #9;
    writeln(arch, linea);

    for i:= filaIni to filaFin do
    begin
      fila:= datos[i];
      linea:= DateTimeToIsoStr(fila[0]) + #9;
      for j:= 0 to high(cols) do
        linea:= linea + FloatToStr(fila[cols[j]]) + #9;
      Writeln(arch, linea);
    end;
  finally
    CloseFile(arch);
  end;
end;

procedure TResultadoOpt.exportarAxlt(const nomVarLibre: String; const fechaIni, fechaFin: TDateTime);
var
  filaIni, filaFin: Integer;
  cols: TDAofNInt;
begin
  recalcRangoFechas(fechaIni, fechaFin, filaIni, filaFin);
  cols:= recalcCols(nomVarLibre);

  exportarAxlt(nomVarLibre, filaIni, filaFin, cols);
end;

function TResultadoOpt.hallarX(nivel: NReal; const ys: TDAofNReal; colIniYs, colFinYs: Integer; const xs: TDAofNReal): NReal;
var
  i, indice0, indice1: Integer;
  delta: NReal;
begin
  if nivel > ys[colIniYs] then
    result:= xs[colIniYs mod length(xs)]
  else if nivel <= ys[colFinYs] then
    result:= xs[colFinYs mod length(xs)]
  else
  begin
    result:= 0; //para sacar el warning
    for i:= colIniYs to colFinYs -1 do
    begin
      if (ys[i] >= nivel) and
         (nivel > ys[i + 1]) then
      begin
        indice0:= i mod length(xs);
        indice1:= (i + 1) mod length(xs);

        delta:= (nivel - ys[i]) / (ys[i + 1] - ys[i]);
        result:= xs[indice0] + (xs[indice1] - xs[indice0]) * delta;
        break;
      end;
    end;
  end;
end;

procedure TResultadoOpt.compararVariableContraCortes(const nombreVar: String; const cortes: TDAofNReal; const filaIni, filaFin: Integer; archivoSalida: String);
const
  coef = 0.87 * 9.8 * 1000 / 3600;
var
  i, j: Integer;
  invCEs: TDAofNReal;
  descVar: TDescVar;
  datosConvertidos, res: TMatOfNReal;
  archiRes: String;
  f: TextFile;
begin
  descVar:= getDescVar(nombreVar);

  SetLength(invCEs, nEstrellasPorPuntoT);
  for i:= 0 to nEstrellasPorPuntoT - 1 do
//    invCEs[i]:= 1 / ((descVarLibre.discretizacionesTraduccion[i mod descVarLibre.nDiscs] - 5.05) * coef);
    invCEs[i]:= 1 / ((descVar.discretizacionesTraduccion[i mod descVar.nDiscs] - 5.05) * coef);

  //Convierto los datos
  SetLength(datosConvertidos, filaFin - filaIni + 1);
  for i:= filaIni to filaFin do
  begin
    SetLength(datosConvertidos[i], length(datos[i]) - 1); //Hay uno mas por la fecha
    for j:= 1 to high(datos[i]) do
      datosConvertidos[i][j - 1]:= datos[i][j] * invCEs[j-1];
  end;

  //Busco las alturas de corte
  SetLength(res, length(datosConvertidos));
  for i:= 0 to high(datosConvertidos) do
  begin
    SetLength(res[i], 5); //Fijo, despues hay que ver como se generaliza
    for j:= 0 to high(res[i]) do
      res[i][j]:= hallarX(cortes[0], datosConvertidos[i], j * descVar.nDiscs, (j + 1) * descVar.nDiscs - 1, descVar.discretizacionesTraduccion);
  end;

  AssignFile(f, archivoSalida);
  try
    Rewrite(f);
    writeln(f, 'Nivel', #9, cortes[0]);
    writeln(f, 'Fecha', #9, 'Estado_H1', #9, 'Estado_H2', #9, 'Estado_H3', #9, 'Estado_H4', #9, 'Estado_H5');
    for i:= 0 to High(res) do
      Writeln(f, DateTimeToIsoStr(datos[filaIni + i][0]), #9, TDAOfNRealToTabbedString(res[i], 10, 2));
    CloseFile(f);      
  Except
    CloseFile(f);
    raise;
  end;
end;

procedure TResultadoOpt.compararVariableContraCortes(const nombreVar: String; const cortes: TDAofNReal; const fechaIni, fechaFin: TDateTime; archivoSalida: String);
var
  filaIni, filaFin: Integer;
begin
  recalcRangoFechas(fechaIni, fechaFin, filaIni, filaFin);

  compararVariableContraCortes(nombreVar, cortes, filaIni, filaFin, archivoSalida);
end;

procedure TResultadoOpt.leerEncabezados(var f: TextFile);
var
  linea, nomvar, nomVarTraduccion, unidades, unidadesTraduccion: String;
  i, j, nDiscsVar, incEstadoCada: Integer;
  discVar, discVarTraduccion: TDAofNReal;
begin
  //Version del simulador
  readln(f, linea);

  //fActPaso
  readln(f, linea);
  NextPal(linea);
  fActPaso:= NextFloat(linea);

  //nContinuas
  readln(f, linea);
  NextPal(linea);
  nVarsContinuas:= NextInt(linea);

  //nDiscretas
  readln(f, linea);
  NextPal(linea);
  nVarsDiscretas:= NextInt(linea);

  //nEstrellas/PuntoT
  readln(f, linea);
  NextPal(linea);
  nEstrellasPorPuntoT:= NextInt(linea);

  //nPuntosT
  readln(f, linea);
  NextPal(linea);
  nPasos:= NextInt(linea);

  incEstadoCada:= 1;
  descVars:= TList.Create;
  descVars.Capacity:= nVarsContinuas + nVarsDiscretas;
  if nVarsContinuas > 0 then
  begin
    hayContinuas:= true;
    //-- Descripción variables contínuas --
    Readln(f, linea);

    for i:= 1 to nVarsContinuas do
    begin
      //Nombre
      readln(f, linea);
      NextPal(linea);
      nomVar:= NextPal(linea);
      if linea <> '' then
        nomVarTraduccion:= NextPal(linea)
      else
        nomVarTraduccion:= '';

      //Unidades
      readln(f, linea);
      NextPal(linea);
      unidades:= NextPal(linea);
      if nomVarTraduccion <> '' then
        unidadesTraduccion:= NextPal(linea);

      //nPuntos
      readln(f, linea);
      NextPal(linea);
      nDiscsVar:= NextInt(linea);

      //x[...]
      readln(f, linea);
      NextPal(linea);
      SetLength(discVar, nDiscsVar);
      for j:= 0 to nDiscsVar - 1 do
        discVar[j]:= NextFloat(linea);
      if nomVarTraduccion <> '' then
      begin
        SetLength(discVarTraduccion, nDiscsVar);
        //xT[...]
        readln(f, linea);
        NextPal(linea);
        for j:= 0 to nDiscsVar - 1 do
          discVarTraduccion[j]:= NextFloat(linea);
      end
      else
        discVarTraduccion:= NIL;

      descVars.Add(TDescVar.Create(nomVar, nomVarTraduccion, unidades, unidadesTraduccion, discVar, discVarTraduccion, incEstadoCada));
      incEstadoCada:= incEstadoCada * nDiscsVar;
    end;
  end
  else
    hayContinuas:= false;

  if nVarsDiscretas > 0 then
  begin
    hayDiscretas:= true;
    //-- Descripción variables contínuas --
    Readln(f, linea);

    for i:= 1 to nVarsDiscretas do
    begin
      //Nombre
      readln(f, linea);
      NextPal(linea);
      nomVar:= NextPal(linea);
      if linea <> '' then
        nomVarTraduccion:= NextPal(linea)
      else
        nomVarTraduccion:= '';

      //Unidades
      readln(f, linea);
      NextPal(linea);
      unidades:= NextPal(linea);
      if nomVarTraduccion <> '' then
        unidadesTraduccion:= NextPal(linea);

      //nPuntos
      readln(f, linea);
      NextPal(linea);
      nDiscsVar:= NextInt(linea);

      //x[...]
      readln(f, linea);
      NextPal(linea);
      SetLength(discVar, nDiscsVar);
      for j:= 0 to nDiscsVar - 1 do
        discVar[j]:= NextFloat(linea);
      if nomVarTraduccion <> '' then
      begin
        SetLength(discVarTraduccion, nDiscsVar);
        //xT[...]
        readln(f, linea);
        NextPal(linea);
        for j:= 0 to nDiscsVar - 1 do
          discVarTraduccion[j]:= NextFloat(linea);
      end
      else
        discVarTraduccion:= NIL;

      descVars.Add(TDescVar.Create(nomVar, nomVarTraduccion, unidades, unidadesTraduccion, discVar, discVarTraduccion, incEstadoCada));
      incEstadoCada:= incEstadoCada * nDiscsVar;
    end;
  end
  else
    hayDiscretas:= false;

  //linea en blanco
  readln(f, linea);

  //linea con los encabezados
  readln(f, linea);    
end;

procedure TResultadoOpt.leerDatos(var f: TextFile);
var
  linea: String;
  i, j: Integer;
begin
  SetLength(datos, nPasos);
  for i:= nPasos - 1 downto 0 do
  begin
    SetLength(datos[i], nEstrellasPorPuntoT + 1);
    readln(f, linea);
    //Paso
    NextPal(linea);
    datos[i][0]:= uFechas.IsoStrToDateTime(nextTab(linea));
//      fila[0]:= trunc(StrToDate(NextPal(linea)));
    for j:= 1 to nEstrellasPorPuntoT do
      datos[i][j]:= NextFloat(linea);
  end;
end;

end.
