unit uAgenda;

interface

uses
  xMatDefs, uFechas, Classes,
  uCosa, uGlobs, uFuentesAleatorias,
  uconstantesSimSEE, SysUtils;

type

  TEmbarque = class(TCosa)
  public
    DiaIngresoEmbarque: NInt; //En el caso de que los embarques no sean periódicos,
    sCuentaEmbarque: string; //Cuentas que pertenecen a cada embarque URU;ARG; etc.

    V_ToP: NReal; // [unidad e Volumen] Volumen comprometido por el contrato Take Or Pay
    V_ToP_util: NReal; // [unidad e Volumen] Volumen comprometido por el contrato Take Or Pay
    P_ToP: NReal;
    // [U$] Precio por Unidad de Volumen de combustible dentro del comprometido por Take or Pay

    constructor Create(DiaIngresoEmbarque: NInt; sCuentaEmbarque: string;
      V_ToP: NReal; V_ToP_util: NReal; P_ToP: NReal);

    constructor Create_from_string(s: string; V_ToP: NReal; P_ToP: NReal);
    function ToString(): string;
    function isValid(): boolean;


  end;

  { TAgendaEmbarque }

  TAgendaEmbarque = class(TCosa)
  public

    ListaEmbarques: TListaDeCosas;
    ListaEmbarquesMod: TListaDeCosas;
    strListaEmbarques: string;

    perturbacionDias: TFuenteAleatoria;
    nroBornePerturbacionDias: integer;

    FechaInicio, FechaFin: TFecha;
    FechaAnt: TDateTime;

    T_ToP: NInt; // [dias] Tiempo de Take or Pay.

    EmbarqueActual: integer;

    EmbarquesPeriodicos: boolean; //Indica si los embarques son periódicos o no.

    V_ToP: NReal; // [unidad e Volumen] Volumen comprometido por el contrato Take Or Pay
    P_ToP: NReal;
    // [U$] Precio por Unidad de Volumen de combustible dentro del comprometido por Take or Pay



    constructor Create(capa: integer; fechaIni: TFecha; T_ToP: NInt;
      V_ToP: NReal; P_ToP: NReal; EmbarquesPeriodicos: boolean; strListaEmbarques: string);
    //    procedure WriteToText( f: TArchiTexto );

    function ToString(): string;
    function GetStrCuentasEmbarques(): string;

    procedure registrarPerturbacionDias(perturbacionDias: TFuenteAleatoria;
      nroBornePerturbacionDias: integer);

    //    procedure GetFechaSiguienteToP(fechaActual : TDateTime; var P_ToP, V_ToP, V_ToP_util: NReal; var fecha_ToP_sig: TDateTime; var sCuenta: String);
    procedure GetFechaSiguienteToP(fechaActual: TDateTime;
      var P_ToP, V_ToP, V_ToP_util: NReal; var fecha_ToP_sig: TDateTime;
      var sCuenta: string; globs: TGlobs);
    procedure GetFechaIntermediaSiguienteToP(fechaActual: TDateTime;
      var fecha: TDateTime; globs: TGlobs);
    procedure Free;

    procedure ultimoCargamento(fecha: TFecha);

    procedure perturbacionAgenda();

  private
    procedure CreateListaEmbarques(capa: integer; s: string);

  end;

procedure AlInicio;
procedure AlFinal;

implementation

uses DateUtils, UCosaConNombre, StrUtils;

constructor TEmbarque.Create(DiaIngresoEmbarque: NInt;
  sCuentaEmbarque: string; V_ToP: NReal; V_ToP_util: NReal; P_ToP: NReal);
begin
  self.DiaIngresoEmbarque := DiaIngresoEmbarque;
  self.sCuentaEmbarque := sCuentaEmbarque;
  self.V_ToP := V_ToP;
  self.V_ToP_util := V_ToP_util;
  self.P_ToP := P_ToP;
end;

constructor TEmbarque.Create_from_string(s: string; V_ToP: NReal; P_ToP: NReal);
var
  s_aux1, s_aux2: string;
  list_str: TStringList;
  compoundString: boolean;

begin
  self.DiaIngresoEmbarque := 0;
  self.sCuentaEmbarque := '';
  self.V_ToP := 0;
  self.V_ToP_util := 0;
  self.P_ToP := 0;

  if length(s) > 0 then
  begin
    compoundString := AnsiContainsStr(s, '(') and AnsiContainsStr(s, ')') and
      AnsiContainsStr(s, '|');
    if compoundString then
    begin
      s_aux1 := AnsiReplaceStr(s, '(', '');
      s_aux2 := AnsiReplaceStr(s_aux1, ')', '');
      list_str := TStringList.Create;
      list_str.Delimiter := '|';
      list_str.DelimitedText := s_aux2;

      if list_str.Count < 4 then
        raise Exception.Create('Faltan campos en cargamento')
      else if list_str.Count = 4 then
      begin
        self.DiaIngresoEmbarque := StrToInt(list_str[0]);
        self.sCuentaEmbarque := list_str[1];
        self.V_ToP := StrToFloat(list_str[2]);
        self.V_ToP_util := self.V_ToP;
        self.P_ToP := StrToFloat(list_str[3]);
      end
      else if list_str.Count = 5 then
      begin
        self.DiaIngresoEmbarque := StrToInt(list_str[0]);
        self.sCuentaEmbarque := list_str[1];
        self.V_ToP := StrToFloat(list_str[2]);
        self.V_ToP_util := StrToFloat(list_str[3]);
        self.P_ToP := StrToFloat(list_str[4]);
      end;
    end
    else
    begin
      self.DiaIngresoEmbarque := StrToInt(s);
      self.sCuentaEmbarque := '';
      self.V_ToP := V_ToP;
      self.V_ToP_util := V_ToP;
      self.P_ToP := P_ToP;
    end;
  end;
end;


function TEmbarque.ToString(): string;
begin
  ToString := concat('(', IntToStr(self.DiaIngresoEmbarque), '|',
    self.sCuentaEmbarque, '|', FloatToStr(self.V_ToP), '|',
    FloatToStr(self.V_ToP_util), '|', FloatToStr(self.P_ToP), ');');
end;

function TEmbarque.isValid(): boolean;
begin
  isValid := (self.DiaIngresoEmbarque <> 0) and (self.V_ToP <> 0) and (self.P_ToP <> 0);
end;


constructor TAgendaEmbarque.Create(capa: integer; fechaIni: TFecha;
  // periodicidad : TPeriodicidad ;
  T_ToP: NInt; V_ToP: NReal; P_ToP: NReal; EmbarquesPeriodicos: boolean;
  strListaEmbarques: string);
begin
  inherited Create(capa);
  self.T_ToP := T_ToP;
  self.V_ToP := V_ToP;
  self.P_ToP := P_ToP;
  self.EmbarquesPeriodicos := self.EmbarquesPeriodicos;
  self.EmbarqueActual := 0;
  self.strListaEmbarques := strListaEmbarques;
  self.FechaInicio := fechaIni;
  self.CreateListaEmbarques(capa, strListaEmbarques);
  self.FechaAnt := fechaIni.dt;
  self.ListaEmbarquesMod := TListaDeCosas.Create(capa, 'agendaSim');

  self.perturbacionDias := nil;
  self.nroBornePerturbacionDias := 0;

end;

//procedure TAgendaEmbarque.WriteToText( f: TArchiTexto );
//var
//  i: NInt;
//  s, aux:string;
//  emb: TEmbarque;
//begin
//  s := '';
//  for i := 0 to ListaEmbarques.Count-1 do
//  begin
//    emb := ListaEmbarques.items[i] As TEmbarque;
//    aux := concat(s, emb.ToString(), ' ');
//    s := aux;
//  end;
//  f.wr('DiasIngresoEmbarques', s);
//end;

function TAgendaEmbarque.ToString(): string;
var
  i: NInt;
  s, aux: string;
  emb: TEmbarque;
begin
  s := '';
  for i := 0 to ListaEmbarques.Count - 1 do
  begin
    emb := ListaEmbarques.items[i] as TEmbarque;
    aux := concat(s, emb.ToString(), ' ');
    s := aux;
  end;
  ToString := s;
end;

function TAgendaEmbarque.GetStrCuentasEmbarques(): string;
var
  i: NInt;
  s, aux: string;
  emb: TEmbarque;
begin
  s := '';
  for i := 0 to ListaEmbarques.Count - 1 do
  begin
    emb := ListaEmbarques.items[i] as TEmbarque;
    aux := concat(s, emb.sCuentaEmbarque, '; ');
    s := aux;
  end;
  GetStrCuentasEmbarques := s;
end;

procedure TAgendaEmbarque.registrarPerturbacionDias(perturbacionDias: TFuenteAleatoria;
  nroBornePerturbacionDias: integer);
begin
  self.perturbacionDias := perturbacionDias;
  self.nroBornePerturbacionDias := nroBornePerturbacionDias;
end;


procedure TAgendaEmbarque.GetFechaSiguienteToP(fechaActual: TDateTime;
  var P_ToP, V_ToP, V_ToP_util: NReal; var fecha_ToP_sig: TDateTime;
  var sCuenta: string; globs: TGlobs);
//procedure TAgendaEmbarque.GetFechaSiguienteToP(fechaActual : TDateTime; var P_ToP, V_ToP: NReal; var fecha_ToP_sig: TDateTime; var sCuenta: String; globs: TGlobs);
var
  cntDias, diasAux, i, diasSiguienteEmbarque, cantidadEmbarques: integer;
  fecha_ToP_ant: TDateTime;
  emb: TEmbarque;
  currLista: TListaDeCosas;

begin

  if globs.EstadoDeLaSala = CES_SIMULANDO then
    currLista := ListaEmbarquesMod
  else
    currLista := ListaEmbarques;

  diasSiguienteEmbarque := MaxInt;
  cntDias := Trunc(fechaActual) - Trunc(self.FechaInicio.dt);
  i := currLista.Count;
  emb := currLista.items[EmbarqueActual] as TEmbarque;

  if FechaAnt <> FechaActual then
  begin
    if FechaAnt < FechaActual then
    begin
      while (cntDias > emb.DiaIngresoEmbarque) and
        (EmbarqueActual < currLista.Count - 1) do
      begin
        EmbarqueActual := EmbarqueActual + 1;
        emb := currLista.items[EmbarqueActual] as TEmbarque;
      end;
    end
    else
    begin
      while (cntDias < emb.DiaIngresoEmbarque) and (EmbarqueActual > 0) do
      begin
        EmbarqueActual := EmbarqueActual - 1;
        emb := currLista.items[EmbarqueActual] as TEmbarque;
      end;
      if (cntDias > emb.DiaIngresoEmbarque) then
      begin
        EmbarqueActual := EmbarqueActual + 1;
        emb := currLista.items[EmbarqueActual] as TEmbarque;
      end;

    end;

  end;
  fecha_ToP_sig := Trunc(self.FechaInicio.dt) + emb.DiaIngresoEmbarque;
  P_ToP := emb.P_ToP;
  V_ToP := emb.V_ToP;
  V_ToP_util := emb.V_ToP_util;
  sCuenta := emb.sCuentaEmbarque;
  FechaAnt := FechaActual;

end;

procedure TAgendaEmbarque.GetFechaIntermediaSiguienteToP(fechaActual: TDateTime;
  var fecha: TDateTime; globs: TGlobs);
//procedure TAgendaEmbarque.GetFechaSiguienteToP(fechaActual : TDateTime; var P_ToP, V_ToP: NReal; var fecha_ToP_sig: TDateTime; var sCuenta: String; globs: TGlobs);
var
  embAnt: TEmbarque;
  currLista: TListaDeCosas;

  P_ToP_aux, V_ToP_aux, V_ToP_util_aux: NReal;
  fecha_ToP_sig, fecha_ToP_ant: TDateTime;
  sCuenta_aux: string;
begin
  if globs.EstadoDeLaSala = CES_SIMULANDO then
    currLista := ListaEmbarquesMod
  else
    currLista := ListaEmbarques;


  GetFechaSiguienteToP(fechaActual, P_ToP_aux, V_ToP_aux, V_ToP_util_aux,
    fecha_ToP_sig, sCuenta_aux, globs);

  if EmbarqueActual > 0 then
  begin
    embAnt := currLista.items[EmbarqueActual - 1] as TEmbarque;
    fecha_Top_ant := Trunc(self.FechaInicio.dt) + embAnt.DiaIngresoEmbarque;
  end
  else
  begin
    fecha_Top_ant := globs.fechaIniOpt.dt;
  end;

  fecha := (fecha_ToP_sig - fecha_ToP_ant) / 2 + Trunc(fecha_ToP_ant);

end;

procedure TAgendaEmbarque.Free;
begin
end;

procedure TAgendaEmbarque.CreateListaEmbarques( capa: integer; s: string);
var
  spliter: TStringList;
  s_trim: string;
  i, Count: integer;
  emb: TEmbarque;
begin
  self.ListaEmbarques := TListaDeCosas.Create( capa, 'agenda');
  s_trim := AnsiReplaceStr(s, ' ', '');
  spliter := TStringList.Create;
  spliter.Delimiter := ';';

  spliter.DelimitedText := s_trim;

  Count := spliter.Count;

  for i := 0 to spliter.Count - 1 do
  begin
    try
      emb := TEmbarque.Create_from_string(spliter[i], self.V_ToP, self.P_ToP);
      if emb.isValid() then
        self.ListaEmbarques.Add(emb);
    except


      on E: Exception do
      begin
        raise Exception.Create('Error leyendo agenda en cargamento ' +
          IntToStr(i) + ': ' + E.Message);
      end;
    end;

  end;

end;

function compEmb(Item1, Item2: Pointer): integer;
var
  res: integer;
begin
  if (TEmbarque(Item1).DiaIngresoEmbarque < TEmbarque(Item2).DiaIngresoEmbarque) then
    res := -1
  else if (TEmbarque(Item1).DiaIngresoEmbarque >
    TEmbarque(Item2).DiaIngresoEmbarque) then
    res := 1
  else
    res := 0;
  compEmb := res;
end;

procedure TAgendaEmbarque.ultimoCargamento(fecha: TFecha);
var
  cantDias, lastEmb: NInt;
  emb, Nemb: TEmbarque;
begin
  cantDias := Trunc(fecha.dt) - Trunc(self.FechaInicio.dt);
  lastEmb := ListaEmbarques.Count - 1;
  emb := ListaEmbarques.items[lastEmb] as TEmbarque;
  if emb.DiaIngresoEmbarque < cantDias then
  begin
    Nemb := TEmbarque.Create(cantDias, emb.sCuentaEmbarque, emb.V_ToP,
      emb.V_ToP_util, emb.P_ToP);
    self.ListaEmbarques.Add(Nemb);
  end;
end;


procedure TAgendaEmbarque.perturbacionAgenda();
var
  modifDia, i, j: integer;
  newEmb, emb: TEmbarque;
  colision: boolean;
begin
  if perturbacionDias <> nil then
  begin
    ListaEmbarquesMod.Free;
    ListaEmbarquesMod := TListaDeCosas.Create(capa, 'agendaSim');

    perturbacionDias.SorteosDelPaso(True);
    modifDia := round(perturbacionDias.Bornera[nroBornePerturbacionDias]);
    for i := 0 to self.ListaEmbarques.Count - 1 do
    begin
      emb := ListaEmbarques.items[i] as TEmbarque;
      newEmb := TEmbarque.Create(emb.DiaIngresoEmbarque + modifDia,
        emb.sCuentaEmbarque, emb.V_ToP, emb.V_ToP_util, emb.P_ToP);
      colision := True;
      while (colision) and (ListaEmbarquesMod.Count > 0) do
      begin
        j := 0;
        repeat
          emb := ListaEmbarquesMod.items[j] as TEmbarque;
          j := j + 1;
        until (newEmb.DiaIngresoEmbarque = emb.DiaIngresoEmbarque) or
          (j = ListaEmbarquesMod.Count);
        if j = ListaEmbarquesMod.Count then
          colision := False
        else
        begin
          perturbacionDias.SorteosDelPaso(True);
          modifDia := round(perturbacionDias.Bornera[nroBornePerturbacionDias]);
          newEmb.DiaIngresoEmbarque := newEmb.DiaIngresoEmbarque + modifDia;
        end;
      end;
      ListaEmbarquesMod.Add(newEmb);
      perturbacionDias.SorteosDelPaso(True);
      modifDia := round(perturbacionDias.Bornera[nroBornePerturbacionDias]);
    end;
    i := ListaEmbarques.Count;
    j := ListaEmbarquesMod.Count;
    ListaEmbarquesMod.Sort(compEmb);
    modifDia := ListaEmbarquesMod.Count;
  end
  else
    ListaEmbarquesMod := ListaEmbarques;
  modifDia := ListaEmbarquesMod.Count;
end;

procedure AlInicio;
begin
{
  registrarClaseDeCosa( TGNLSumComb_TakeOrPay_Spot.ClassName, TGNLSumComb_TakeOrPay_Spot );
  registrarClaseDeCosa( TAgendaEmbarque.ClassName, TAgendaEmbarque );
}
end;


procedure AlFinal;
begin
end;


end.
