unit uensembles;

{$mode delphi}

interface

uses
  {$IFDEF GRAFICAR_ENSEMBLES}
    ugraficador,
//    graphics,
    usvgcanvas,
  {$ENDIF}
  {$IFDEF PRONO_HIDRO_IMFIA}
    uestadosinuy,
  {$ENDIF}
  Classes,
  matreal,
  SysUtils,
  uparseutils,
  ufechas,
  ucosa,
  math,
  xmatdefs;

type

    { TEnsembles }

    TEnsembles = class

    Nombre : String;
    NCanales : integer;
    Nmiembros : integer;
    Npasos : integer;
    dtPrimeraMuestra : TDateTime;
    dtPaso : TDateTime;
    EnsemblesMat : array of TMatR;
    nombresEnsembles : array of string;

    constructor Create(unNombre : string; NumCanales,NumMiembros,NumPasos : integer; dtIni,paso : TDateTime);

    constructor CreateFromArchiXLT(archi:string);

{$IFDEF PRONO_HIDRO_IMFIA}
    procedure CargarDesdeEstado(estado:TEstado_SIN_UY);
    {  Los ensembles de Salto Grande y Rio Negro se calculan en uestadosinuy a
       un estado. Puede ser util que se cargue desde alli. NO ES GENERICO, ES
       PARA CARGAR EL ENSAMBLE DE SG CMT Y LOS DEL IMFIA. }
{$ENDIF}

    procedure EnsembleDesdeMatR(nombre:string;kEnsemble:integer;MatrizEnsemble:TMatR);
    //  Esto se usa para cargar todos los miembros de un canal desde un TMatR.

    function ToCortesDePE( Cortes: array of NReal ) : TEnsembles;
    //  Devuelve un Tensembles que contiene los cortes de PE de los ensembles
    //  pasados en el array de cortes de PE, Cortes

    procedure WriteToArchiXLT(archi:string);

{$IFDEF GRAFICAR_ENSEMBLES}

    procedure Graficar(canal:integer;dir:string);
    //  Grafica un ensemble completo salida en svg

    procedure GraficarComparar(Ensembles1,Ensembles2: TEnsembles; canal:integer; dir:string);
    {  Grafica en un mismo archivo svg cada canal de cada uno de los ensembles.
      Se le puede indicar un canal si se quiere graficar un canal solo, si
      se quiere graficar todos los canales se carga como canal -1  }
{$ENDIF}
    procedure Free;

    end;

procedure LlenarHuecos(var Matriz : TMatR);

implementation

procedure LlenarHuecos(var Matriz : TMatR);
var
  jmiembro, kpaso, pasoIniRelleno : integer;
  anteriorValido, D_horario : NReal;

begin

for jmiembro:=1 to Matriz.nc do
  begin

  kpaso := 1;
  //  Leer los datos hasta llegar a un no valido
  while ( (kpaso < Matriz.nf) and (Matriz.e(kpaso, jmiembro) > 0)) do
    begin
    kpaso := kpaso + 1;
    end;

  //  Asumo como el ultimo dato valido 4 datos para atras, para dejar margen
  pasoIniRelleno := Max(kpaso - 4, 1);
  anteriorValido := Matriz.e(pasoIniRelleno, jmiembro );
  D_horario := 0.997879187;  //  Decaimiento horario para un D semanal de 0.70
                             //  D_horario 0.7^( 1/(24*7) ) = 0.997879187

  for kpaso := pasoIniRelleno to Matriz.nf do
    begin
    Matriz.pon_e(kpaso, jmiembro, anteriorValido);
    anteriorValido := anteriorValido*D_horario;
    end;
  end;

end;


{ TEnsembles }

constructor TEnsembles.Create(unNombre: string; NumCanales,
  NumMiembros,NumPasos: integer; dtIni, paso: TDateTime);
begin
  inherited create;
  Nombre := unNombre;
  NCanales:=NumCanales;
  Nmiembros:=NumMiembros;
  dtPrimeraMuestra:=dtIni;
  dtPaso:=paso;
  Npasos:=NumPasos;

  SetLength(nombresEnsembles,NCanales);
  SetLength(EnsemblesMat,NCanales);
end;

constructor TEnsembles.CreateFromArchiXLT(archi: string);
{1-  Leo el encabezado. Saco version, nombre, numero de canales, numero de
    miembros, numero de pasos, fecha de primera muestra, paso entre muestras.
 2-  Chequeo la version.
 3-  Creo un Tensembles vacio.
 4-  Leo cada ensemble. Primera wl nombre. Luego matriz de Npasos x Nmiembros.
 5-  Cargo los ensembles al Tensembles.
}

var
Nm, Nc, Np, Nversion , hcanal, kpaso, jmiembro: integer;
NombreFuente,linea,nombreCanal , pal: string;
dtPM,dtP:TDateTime;
Texto: TextFile;
MatCanal: TMatR;
val : NReal;

begin
// 1-
AssignFile(Texto,archi);
Reset(Texto);

readln(Texto,linea);
nextpal(linea,#9);
Nversion := StrToInt(linea);

readln(Texto,linea);
nextpal(linea,#9);
NombreFuente :=  linea;

readln(Texto,linea);
nextpal(linea,#9);
Nc := StrToInt(linea);

readln(Texto,linea);
nextpal(linea,#9);
Nm := StrToInt(linea);

readln(Texto,linea);
nextpal(linea,#9);
Np := StrToInt(linea);

readln(Texto,linea);
NextPal(linea,#9);
dtPM :=  IsoStrToDateTime( linea );

readln(Texto,linea);
NextPal(linea,#9);
dtP := StrToFloat(linea) ;


// 2-
If Nversion <= version_ArchiTexto then
   begin
   // 3-
   Create(NombreFuente,Nc,Nm,Np,dtPM,dtP) ;

   // 4-
   for hcanal:=0 to Nc-1 do
     begin
     readln(Texto,linea);
     readln(Texto, nombreCanal);
     MatCanal := TMatR.Create_Init(Np,Nm);

     for kpaso:=1 to Np do
         begin
         readln(Texto, linea);
         for jmiembro := 1 to Nm do
           begin
           pal := nextpal(linea,#9);
           val := StrToFloat(pal);
           MatCanal.pon_e(kpaso, jmiembro, val);
           end;
         end;
     // 5-
     EnsembleDesdeMatR(nombreCanal,hcanal,MatCanal);
     MatCanal.free;
     end;
   end;
end;

{$IFDEF PRONO_HIDRO_IMFIA}
procedure TEnsembles.CargarDesdeEstado(estado: TEstado_SIN_UY);
{1-  Cargo los nombres de los ensembles
 2-  Cargo para Bonete y Palmar los ensembles calculado mediante el modelo del
     IMFIA.
 3-  Cargo los ensembles pasados por SG CTM en el ensemble
 4-  Paso la lectura string serializado de el IN34 a vector para quedarme con el
     primer valor. Dejo constante como el valor del primer dato.
 5-  Cargo como constante igual a 1 el valor del cmo1 y cmo3 de Brasil para todo
     el periodo de pronostico.
}
var
vectIN34:TVectR;
kEnsemble: Integer;

begin


{  Paso los valores de el Nino (in34) leido como string serializado a vector
  Me voy a quedar con el primer valor de el nino como el valor para todo
  el periodo del pronos }
  vectIN34:= TVectR.Create_unserialize_pg(estado.iN34_str);

  //  1-  Nombres
  nombresEnsembles[0]:= 'Bonete';
  nombresEnsembles[1]:= 'Palmar';
  nombresEnsembles[2]:= 'SG';
  nombresEnsembles[3]:= 'in34';
  nombresEnsembles[4]:= 'cmo1';
  nombresEnsembles[5]:= 'cmo3';

  //  2-  Bonete y Palmar IMFIA
  EnsemblesMat[0]:=TMatR.Create_Clone(estado.QBonIMFIAmatHora);
  EnsemblesMat[1]:=TMatR.Create_Clone(estado.QPalIMFIAmatHora);
  //  3-  SG Ensemble CTM Oficial
  EnsemblesMat[2]:=TMatR.Create_Clone(estado.QSGOficialMatHr);
  //  4-  IN34
  EnsemblesMat[3]:=TMatR.Create_InitVal( estado.QBonIMFIAmatHora.nf,
                                      estado.QBonIMFIAmatHora.nc, vectIN34.e(1) );

  {$IFDEF CARGAR_CMO}
  //  5-  cmo1 y cmo3 Brasil constantes
  //  raise Exception.create('FALTA IMPLEMENTAR CARGAR EL CMO');

  EnsemblesMat[4] := estado.cmo1hr;
  EnsemblesMat[5] := estado.cmo3hr;
  {$ELSE}
  //  5-  cmo1 y cmo3 Brasil constantes 1
  EnsemblesMat[4]:=TMatR.Create_InitVal( estado.QBonIMFIAmatHora.nf,
                                      estado.QBonIMFIAmatHora.nc ,1);
  EnsemblesMat[5]:=TMatR.Create_InitVal( estado.QBonIMFIAmatHora.nf,
                                      estado.QBonIMFIAmatHora.nc ,1);
  {$ENDIF}

  for kEnsemble:=0 to 5 do
    LlenarHuecos(EnsemblesMat[kEnsemble]);

  vectIN34.free;

  Npasos:=min(estado.QBonIMFIAmatHora.nf,estado.QSGOficialMatHr.nf);

end;
{$ENDIF}


procedure TEnsembles.Free;

var
  hcanal: Integer;
begin

  for hcanal:=0 to high(EnsemblesMat) do
       begin
       if EnsemblesMat[hcanal]<> nil then
          EnsemblesMat[hcanal].Free;
       end;
   SetLength(EnsemblesMat,0);
   inherited free;
end;

procedure TEnsembles.EnsembleDesdeMatR(nombre: string; kEnsemble: integer;
  MatrizEnsemble: TMatR);
begin

  if kEnsemble < NCanales then
     begin
     nombresEnsembles[kEnsemble] := nombre;
     EnsemblesMat[kEnsemble] := TMatR.Create_Clone(MatrizEnsemble);
     end
  else
     begin
     NCanales := kEnsemble+1;
     SetLength(nombresEnsembles,NCanales);
     SetLength(EnsemblesMat,NCanales);

     nombresEnsembles[kEnsemble] := nombre;
     EnsemblesMat[kEnsemble]:= TMatR.Create_Clone( MatrizEnsemble );

     writeln('Atencion!! Se ha aumentado el numero de ensembles de '
             + IntToStr(NCanales) + ' a ' + IntToStr(kEnsemble+1) + '.');
     end;

end;

function TEnsembles.ToCortesDePE(Cortes: array of NReal): TEnsembles;

var
ensemblesPE : TEnsembles;
NCortes , kCorte, hcanal, kColumna: integer;
matCortesPE, matOrdenada: TMatR;
columnaPE: TVectR;

begin
//  Crear el ensembles
  Ncortes:=length(Cortes);
  ensemblesPE:= TEnsembles.Create( Self.Nombre, self.NCanales,
                                   NCortes, Self.Npasos, self.dtPrimeraMuestra,
                                   self.dtPaso );
//  Armar la matriz de cada canal
  for hcanal:=0 to self.NCanales-1 do
    begin
    //  Armar la matriz ordenada de donde se van a sacar los cortes de PE
    matCortesPE := TMatR.Create_Init(self.Npasos,Ncortes);
    matOrdenada := TMatR.Create_Clone( self.EnsemblesMat[hcanal] ) ;
    matOrdenada.Sort ;

    for kCorte:=0 to Ncortes-1 do
      begin
      //  Cargar cada columna de la matriz ordenada como un corte de PE
      kColumna := round(self.Nmiembros*Cortes[kCorte]);
      columnaPE := self.EnsemblesMat[hcanal].Columna(kColumna);
      matCortesPE.pon_columna( 1, kCorte+1, columnaPE.toTDAOfNReal );
      end;

    //  Cargar la matriz de PE en el canal de los ensembles
    ensemblesPE.EnsemblesMat[hcanal] := TMatR.Create_Clone(matCortesPE);
    ensemblesPE.nombresEnsembles[hcanal] := self.nombresEnsembles[hcanal] ;
    matOrdenada.free;
    matCortesPE.Free;
    end;

  //  retornar los ensembles
  Result := ensemblesPE;

end;

procedure TEnsembles.WriteToArchiXLT(archi:string);
var
Texto : TextFile;
hcanal, jpaso, kmiembro: Integer;
begin
     AssignFile(Texto,archi);
     Rewrite(Texto);

     // Encabezado
     writeln(Texto,'Version: ' +#9 + IntToStr(VERSION_ArchiTexto));
     writeln(Texto,'Fuente: ' +#9 + Nombre);
     writeln(Texto,'Ncanales: ' +#9 + IntToStr(NCanales));
     writeln(Texto,'Nmembers: ' +#9 + IntToStr(Nmiembros));
     writeln(Texto,'Npasos: ' +#9 + IntToStr(Npasos));
     writeln(Texto,'dt Primera Muestra: ' +#9 + DateTimeToIsoStr(dtPrimeraMuestra));
     writeln(Texto,'Paso: ' +#9 + FloatToStr(dtPaso));
     writeln(Texto);

     // Escribir ensambles
     for hcanal:=0 to NCanales-1 do
       begin
       if EnsemblesMat[hcanal]<>nil then
          begin
          writeln(Texto,nombresEnsembles[hcanal]);
          for jpaso:=1 to Npasos do
            begin
            for kmiembro:=1 to Nmiembros-1 do
                begin
                write(Texto,FloatToStr(EnsemblesMat[hcanal].e(jpaso,kmiembro))+#9);
                end;
            writeln(Texto, FloatToStr(EnsemblesMat[hcanal].e(jpaso,Nmiembros)));
            end;
          writeln(Texto)
          end;
       end;

     CloseFile(Texto);

end;


{$IFDEF GRAFICAR_ENSEMBLES}

procedure TEnsembles.Graficar(canal: integer; dir: string);
var
grafico:TGrafico;
SeriesY : array of TSerieGrafico;
jmiembro, hcanal, kPaso: Integer;
X:TDAofNReal;
SerieX: TSerieGrafico;
begin
  // En caso de que el canal que se pasa sea -1 se grafican todos los canales
  if canal=-1 then
  begin
     for hcanal:=0 to NCanales-1 do
     begin
     grafico := TGrafico.Create(nombresEnsembles[hcanal], '',TG_DispersionXY,0);
     grafico.Titulo := nombresEnsembles[hcanal] ;
     grafico.FontSize:=15;
     grafico.eje_x.flg_mostrar_lineas:=true;
//     grafico.eje_x.titulo := 'El nombre de las x';
//     grafico.eje_y.titulo := 'El nombre de las y';
     grafico.eje_x.flg_max_auto:=true;
     grafico.eje_x.flg_min_auto:=true;
     grafico.eje_y.flg_max_auto:=true;
     grafico.eje_y.flg_min_auto:=true;
     grafico.eje_x.FloatFormat_NDecimales := 0;

     //  Armo la serie X
     SetLength(X,Npasos);
     for kPaso:=0 to Npasos-1 do
         X[kpaso]:=dtPrimeraMuestra + dtPaso*kPaso;
     SerieX:=TSerieGrafico.Create('paso',X,nil,grafico,'YYYY-MM-DD',true);

     TSerieGrafico.Create();
     grafico.AddSerie( SerieX);
     grafico.eje_x.DateFormatStr:='YYYY-MM-DD';
     //  Armo y guardo la serie de cada miembro
     setlength(SeriesY,Nmiembros);
     for jmiembro:=0 to Nmiembros-1 do
       begin
         SeriesY[jmiembro] := TSerieGrafico.Create('miembro_'+IntToStr(jmiembro),
                   EnsemblesMat[hcanal].Columna(jmiembro+1), SerieX, grafico,'##.##');
         grafico.AddSerie(SeriesY[jmiembro]);
         SeriesY[jmiembro].RadioMarcador:=0;
         SeriesY[jmiembro].AnchoLineaMarcador:=0;
         SeriesY[jmiembro].AnchoLinea:=3;
       end;
     grafico.eje_x.flg_min_auto:=true;
     grafico.eje_x.flg_max_auto:=true;
     grafico.eje_y.flg_min_auto:=true;
     grafico.eje_y.flg_max_auto:=true;
     grafico.SaveSVG(dir+DirectorySeparator+nombresEnsembles[hcanal]+'.svg',1280,720);
     grafico.free;
     end;
  end
  else
  begin
    grafico := TGrafico.Create(nombresEnsembles[canal], '',TG_DispersionXY,0);
    grafico.Titulo := nombresEnsembles[canal] ;
    grafico.FontSize:=15;

    //  Armo la serie X
    SetLength(X,Npasos);
    for kPaso:=0 to Npasos-1 do
        X[kpaso]:=dtPrimeraMuestra + dtPaso*kPaso;
    SerieX:=TSerieGrafico.Create('paso',X,nil,grafico,'YYYY-MM-DD',true);


    //  Armo y guardo la serie de cada miembro
    setlength(SeriesY,Nmiembros);
    for jmiembro:=0 to Nmiembros-1 do
    begin
      SeriesY[jmiembro] := TSerieGrafico.Create('miembro_'+IntToStr(jmiembro),
                EnsemblesMat[canal].Columna(jmiembro+1), SerieX, grafico,'##.##');
      grafico.AddSerie(SeriesY[jmiembro]);
      SeriesY[jmiembro].RadioMarcador:=0;
      SeriesY[jmiembro].AnchoLineaMarcador:=0;
      SeriesY[jmiembro].AnchoLinea:=3;
    end;
    grafico.eje_x.flg_min_auto:=true;
    grafico.eje_x.flg_max_auto:=true;
    grafico.eje_y.flg_min_auto:=true;
    grafico.eje_y.flg_max_auto:=true;
    grafico.SaveSVG(dir+DirectorySeparator+nombresEnsembles[canal]+'.svg',1280,720);
    grafico.free;
  end;

end;

procedure TEnsembles.GraficarComparar(Ensembles1, Ensembles2: TEnsembles;
  canal: integer; dir: string);
var
grafico:TGrafico;
SeriesY : array of TSerieGrafico;
jmiembro, hcanal, kPaso, j: Integer;
X:TDAofNReal;
SerieX: TSerieGrafico;
columna: TVectR;
begin
//------------------------------------------------------------------------------
  if canal=-1 then  // Si canal = -1 => se grafican todos los canales
  begin
     for hcanal:=0 to Ensembles1.NCanales-1 do
     begin
     grafico := TGrafico.Create(Ensembles1.nombresEnsembles[hcanal], '',TG_DispersionXY,0);
     grafico.Titulo := Ensembles1.nombresEnsembles[hcanal] ;
     grafico.FontSize:=13;
     grafico.idstr:= 'Calibrado@' + DateTimeToIsoStr(Ensembles1.dtPrimeraMuestra);
     grafico.eje_x.flg_mostrar_lineas:=true;
     grafico.eje_x.flg_max_auto:=true;
     grafico.eje_x.flg_min_auto:=true;
     grafico.eje_y.flg_max_auto:=true;
     grafico.eje_y.flg_min_auto:=true;
     grafico.eje_x.FloatFormat_NDecimales := 0;

     //  Armo la serie X
     SetLength(X,Ensembles1.Npasos);
     for kPaso:=0 to Ensembles1.Npasos-1 do
         X[kpaso]:=Ensembles1.dtPrimeraMuestra + Ensembles1.dtPaso*kPaso;
     SerieX:=TSerieGrafico.Create('paso',X,nil,grafico,'YYYY-MM-DD',true);

     TSerieGrafico.Create();
     grafico.AddSerie( SerieX);
     grafico.eje_x.DateFormatStr:='YYYY-MM-DD';
     //  Armo y guardo la serie de cada miembro
     setlength(SeriesY,Ensembles1.Nmiembros+Ensembles2.Nmiembros);
     for jmiembro:=0 to Ensembles1.Nmiembros-1 do
       begin
       j := jmiembro;
       columna := TVectR.Create_Clone(
                         Ensembles1.EnsemblesMat[hcanal].Columna(jmiembro+1));
       SeriesY[j]:=TSerieGrafico.Create( 'E_1_miembro_'+IntToStr(jmiembro),
                                         columna, SerieX, grafico,'##.##');

       SeriesY[j].RadioMarcador:=1;
       SeriesY[j].AnchoLineaMarcador:=1;
       SeriesY[j].tipoMarcador:= TM_Circulo;
       SeriesY[j].colorFondoMarcador:=SeriesY[j].colorLineaMarcador;
       SeriesY[j].AnchoLinea:=1;
       grafico.AddSerie(SeriesY[j]);
       end;

     for jmiembro:=0 to (Ensembles2.Nmiembros-1) do
       begin
       j := jmiembro+Ensembles1.Nmiembros;
       columna := TVectR.Create_Clone(
                         Ensembles2.EnsemblesMat[hcanal].Columna(jmiembro+1));
       columna.resize(Ensembles1.Npasos);
       SeriesY[j] := TSerieGrafico.Create( 'E_2_miembro_'+IntToStr(jmiembro),
                                           columna, SerieX, grafico,'##.##');
       SeriesY[j].RadioMarcador:=3;
       SeriesY[j].AnchoLineaMarcador:=1;
       SeriesY[j].tipoMarcador:= TM_Cuadro;
       SeriesY[j].colorFondoMarcador:=SeriesY[j].colorLineaMarcador;
       SeriesY[j].AnchoLinea:=8;
       grafico.AddSerie(SeriesY[j]);
       end;

     grafico.eje_x.flg_min_auto:=true;
     grafico.eje_x.flg_max_auto:=true;
     grafico.eje_y.flg_min_auto:=true;
     grafico.eje_y.flg_max_auto:=true;
     grafico.SaveSVG(dir+DirectorySeparator+Ensembles1.nombresEnsembles[hcanal]+'.svg',1280,720);
     grafico.free;
     end;
  end
//------------------------------------------------------------------------------
  else  //  Si canal <> -1 (Grafico un canal solo)
  begin
    grafico := TGrafico.Create(Ensembles1.nombresEnsembles[canal], '',TG_DispersionXY,0);
    grafico.Titulo := Ensembles1.nombresEnsembles[canal] ;
    grafico.FontSize:=13;

    //  Armo la serie X
    SetLength(X,Ensembles1.Npasos);
    for kPaso:=0 to Ensembles1.Npasos-1 do
        X[kpaso]:=Ensembles1.dtPrimeraMuestra + Ensembles1.dtPaso*kPaso;
    SerieX:=TSerieGrafico.Create('paso',X,nil,grafico,'YYYY-MM-DD',true);


    //  Armo y guardo la serie de cada miembro
    setlength(SeriesY,Ensembles1.Nmiembros+Ensembles2.Nmiembros);
    for jmiembro:=0 to Ensembles1.Nmiembros-1 do
    begin
    j := jmiembro;
    columna := TVectR.Create_Clone(
                      Ensembles1.EnsemblesMat[canal].Columna(j+1));
    SeriesY[j]:=TSerieGrafico.Create( 'E_1_miembro_'+IntToStr(jmiembro),
                                      columna, SerieX, grafico,'##.##');
    SeriesY[j].RadioMarcador:=0;
    SeriesY[j].AnchoLineaMarcador:=0;
    SeriesY[j].AnchoLinea:=3;
    grafico.AddSerie(SeriesY[j]);
    end;

    for jmiembro:=0 to (Ensembles2.Nmiembros-1) do
    begin
    j := jmiembro+Ensembles1.Nmiembros;
    columna := TVectR.Create_Clone(
                      Ensembles2.EnsemblesMat[hcanal].Columna(jmiembro+1));
    columna.resize(Ensembles1.Npasos);
    SeriesY[j] := TSerieGrafico.Create( 'E_2_miembro_'+IntToStr(jmiembro),
                                        columna, SerieX, grafico,'##.##');
    SeriesY[j].RadioMarcador:=0;
    SeriesY[j].AnchoLineaMarcador:=0;
    SeriesY[j].AnchoLinea:=3;
    grafico.AddSerie(SeriesY[j]);
    end;

    grafico.eje_x.flg_min_auto:=true;
    grafico.eje_x.flg_max_auto:=true;
    grafico.eje_y.flg_min_auto:=true;
    grafico.eje_y.flg_max_auto:=true;
    grafico.SaveSVG(dir+DirectorySeparator+Ensembles1.nombresEnsembles[canal]+'.svg',1280,720);
    grafico.free;
  end;

end;

{$ENDIF}



end.

