unit uReferenciaMonitor;
  {$MODE Delphi}

interface

uses
  uMonitores, uCosa, uCosaConNombre, uVarDefs, SysUtils, Classes, xMatDefs,
  uSalasDeJuego, uEventosOptSim;

resourcestring
  mesNoSeEncuentraActor = 'No se encuentra el actor';
  mesNoSeEncuentraLaVariable = 'No se encuentra la variable';
  mesEnElActor = ' en el actor ';

type
  TClasesDeMonitores = class of TMonitores;
  AOfVarDef = array of TClasesDeVarDefs;

  TClaseReferenciaMonitor = class of TReferenciaMonitor;

  //Lanzada por el resolver de TReferenciaDefVar si no encuentra la Cosa o la
  //variable referenciada
  EReferenciaDefVarException = class(Exception)
  public
    constructor Create(nombreCosa, claseCosa: string); overload;//No se encuentra la cosa
    constructor Create(nombreCosa, claseCosa, nombreVar: string);
      overload; //No se encuentra la variable
  end;

  //Lanzada por crearMonitorDeVariable monitor si alguna variable no se pudo resolver
  EMonitorException = class(Exception)
  public
    cantVarsResueltas: integer;
    constructor Create(const msg: string; cantVarsResueltas: integer);
  end;

  { TReferenciaDefVar }

  TReferenciaDefVar = class(TCosa)
  public
    nombreCosa, claseCosa, nombreVar: string;

    //    indice : Integer; //-1 si la variable no es un vector
    //-2 si es un vector pero lo quiero visualizar como tal
    //0..n si quiero acceder al elemento indice del vector
    constructor Create(capa: integer; xnombreCosa, xclaseCosa, xnombreVar: string);

    function Rec: TCosa_RecLnk; override;
    procedure BeforeRead(version, id_hilo: integer); override;
    procedure AfterRead(f:TArchiTexto); override;

    function ClaseNombre: string;
    function resolver(cosasMonitoreables: TListaDeCosasConNombre): TVarDef;

    procedure Free; override;
  end;

  TDAOfTReferenciaDefVar = array of TReferenciaDefVar;

  { TReferenciaMonitor }

  TReferenciaMonitor = class(TCosaConNombre)//EL nombre sera el nombre del monitor
  public
    Enabled: boolean;
    eventos: TDAOfEventoProc;

    monitor: TMonitores;
    posEnSala: integer;

    constructor Create(capa: integer; const xNombreMon: string;
      xEventosProcs: TDAOfEventoProc); reintroduce;

    function Rec: TCosa_RecLnk; override;
    procedure BeforeRead(version, id_hilo: integer); override;
    procedure AfterRead(f:TArchiTexto); override;


    //Busca las variables que monitorea en la sala, las prepara y crea el
    //monitor para ellas asignandolo en monitor
    procedure crearMonitorDeVariables(cosasMonitoreables: TListaDeCosasConNombre);
      virtual; abstract;

    function monitoreaA(cosa: TCosaConNombre): boolean; virtual; abstract;

    //Retorna true si el cambio en la cosa afecta a la referencia al monitor
    function notificarCambioCosa(cosaOrig, cosaNueva: TCosaConNombre): boolean;
      virtual; abstract;

    //pos debe ser la posición en la array de la TSalaMonitoreada en que se encuentra
    //el monitor. Sirve para comunicarse entre el monitor y la sala
    procedure notificarPosEnManejador(pos: integer); virtual;

    //Filtra las cosas que no son monitoreables por el monitor. El mismo monitor,
    //otros monitores que lo monitoreen y aquellas que no publican variables
    //monitoreables por el tipo de monitor
    function cosasMonitoreables(cosasConVariablesPublicadas:
      TListaDeCosasConNombre{of TReferenciaMonitor}): TListaDeCosasConNombre;

    //Filtra las cosas que no son monitoreables por la clase de monitor.
    //Aquellas que no publican variables monitoreables por el tipo de monitor
    class function cosasMonitoreablesDeClase(cosasConVariablesPublicadas:
      TListaDeCosasConNombre): TListaDeCosasConNombre;

    //Retorna la lista de nombres de las variables de la cosa que son monitoreables
    //por la clase de referencia
    class function nombresVarsMonitoreables(cosa: TCosaConNombre): TStrings;

    class function tiposMonitoreables: AOfVarDef; virtual; abstract;
    class function puedeMonitorearTipo(varDef: TVarDef): boolean;
    function getReferenciasDefVars: TDAOfTReferenciaDefVar; virtual; abstract;

    function esDeSimulacion: boolean;
    function esDeOptimizacion: boolean;

    procedure Free; override;

    
  private
    px: TDAofDAofString;




  end;

  { TReferenciaMonUniVar }
  TReferenciaMonUniVar_auxRec = class
    nombreCosa: string;
    claseCosa: string;
    nombreVar: string;
  end;

  TReferenciaMonUniVar = class(TReferenciaMonitor)
  public
    defVar: TReferenciaDefVar;

    //      indice : Integer; //-1 si la variable no es un vector
    //-2 si es un vector pero lo quiero tratar como tal
    //0..n si quiero acceder al elemento indice del vector
    constructor Create(capa: integer; xnombreMon, xnombreCosa, xclaseCosa, xnombreVar: string; xEventosProcs: TDAOfEventoProc);

     
    function Rec: TCosa_RecLnk; override;
    procedure BeforeRead(version, id_hilo: integer); override;
    procedure AfterRead(f:TArchiTexto); override;

    function monitoreaA(cosa: TCosaConNombre): boolean; override;
    function notificarCambioCosa(cosaOrig, cosaNueva: TCosaConNombre): boolean; override;
    function getReferenciasDefVars: TDAOfTReferenciaDefVar; override;
    procedure Free; override;
  private
    px: TReferenciaMonUniVar_auxRec;

  end;

  { TReferenciaMonMultiVar }

  TReferenciaMonMultiVar = class(TReferenciaMonitor)
  public
    (**************************************************************************)
    (*               A T R I B U T O S   P E R S I S T E N T E S              *)
    (**************************************************************************)

    defVars: TListaDeCosas {of TReferenciaDefVar};

   (**************************************************************************)
    //    indices : TDAofNInt;//-1 si la variable no es un vector
    //-2 si es un vector pero lo quiero tratar como tal
    //0..n si quiero acceder al elemento indice del vector
    
    constructor Create(capa: integer; xnombreMon: string;
      xEventosProcs: TDAOfEventoProc; defVars: TListaDeCosas);

    function Rec: TCosa_RecLnk; override;
    procedure BeforeRead(version, id_hilo: integer); override;
    procedure AfterRead(f:TArchiTexto); override;

    function monitoreaA(cosa: TCosaConNombre): boolean; override;
    function notificarCambioCosa(cosaOrig, cosaNueva: TCosaConNombre): boolean; override;
    function getReferenciasDefVars: TDAOfTReferenciaDefVar; override;

    procedure Free; override;
  end;

procedure AlInicio;
procedure AlFinal;

implementation

//-------------------------------------
//Metodos de EReferenciaDefVarException
//=====================================

constructor EReferenciaDefVarException.Create(nombreCosa, claseCosa: string);
begin
  inherited Create(mesNoSeEncuentraActor + ' ' + claseCosa + ', ' +
    nombreCosa + '.' + #13);
end;

constructor EReferenciaDefVarException.Create(nombreCosa, claseCosa, nombreVar: string);
begin
  inherited Create(mesNoSeEncuentraLaVariable + ' ' + nombreVar +
    mesEnElActor + claseCosa + ', ' + nombreCosa + '.' + #13);
end;

//----------------------------
//Metodos de EMonitorException
//============================

constructor EMonitorException.Create(const msg: string; cantVarsResueltas: integer);
begin
  inherited Create(msg);
  self.cantVarsResueltas := cantVarsResueltas;
end;

//----------------------------
//Metodos de TReferenciaDefVar
//============================

constructor TReferenciaDefVar.Create(capa: integer;
  xnombreCosa, xclaseCosa, xnombreVar: string{; indice : Integer});
begin
  inherited Create(capa);
  nombreCosa := xnombreCosa;
  claseCosa := xclaseCosa;
  nombreVar := xnombreVar;
  //  self.indice := indice;
end;

function TReferenciaDefVar.Rec: TCosa_RecLnk;
begin
  Result:=inherited Rec;
  Result.addCampoDef('nombreCosa', nombreCosa);
  Result.addCampoDef('claseCosa', claseCosa);
  Result.addCampoDef('nombreVar', nombreVar);
end;

procedure TReferenciaDefVar.BeforeRead(version, id_hilo: integer);
begin
  inherited BeforeRead(version, id_hilo);
end;

procedure TReferenciaDefVar.AfterRead(f:TArchiTexto);
begin
  inherited AfterRead(f);
end;


function TReferenciaDefVar.ClaseNombre: string;
begin
  Result := claseCosa + ', ' + nombreCosa;
end;

function TReferenciaDefVar.resolver(cosasMonitoreables: TListaDeCosasConNombre): TVarDef;
var
  res: TVarDef;
  ipos: integer;
begin
  if cosasMonitoreables.find(claseCosa, nombreCosa, ipos) then
  begin
    res := TCosaConNombre(cosasMonitoreables[ipos]).buscarVariable(nombreVar);
    if res <> nil then
      Result := res
    else
      raise EReferenciaDefVarException.Create(nombreCosa, claseCosa, nombreVar);
  end
  else
  begin
    raise EReferenciaDefVarException.Create(nombreCosa, claseCosa);
    Result := nil;
  end;
end;

procedure TReferenciaDefVar.Free;
begin
  SetLength(nombreCosa, 0);
  SetLength(claseCosa, 0);
  SetLength(nombreVar, 0);
  inherited Free;
end;











//-----------------------------
//Metodos de TReferenciaMonitor
//=============================


constructor TReferenciaMonitor.Create(capa: integer; const xNombreMon: string;
  xEventosProcs: TDAOfEventoProc);
begin
  inherited Create( capa, xNombreMon);
  eventos := xEventosProcs;
  Enabled := True;
  posEnSala := -1;
  pubvarlst := nil;
  monitor := nil;
end;

function TReferenciaMonitor.Rec: TCosa_RecLnk;
begin
  Result:=inherited Rec;
  Result.addCampoDef('enabled', Enabled);
  result.addCampoDef_DAOfDAOfStr_OLD2_( 'nEventos', ['cadena', 'refProc' ], px );
end;

procedure TReferenciaMonitor.BeforeRead(version, id_hilo: integer);
begin
  inherited BeforeRead(version, id_hilo);
  px:= nil;
end;

procedure TReferenciaMonitor.AfterRead(f:TArchiTexto);
var
   i: integer;
   nEventos: integer;
begin
  inherited AfterRead(f);
  monitor := nil;
  posEnSala := -1;
  setlength( eventos, length( px ) );
  for i:= 0 to high( eventos ) do
  begin
    eventos[i].refProc:= StrToInt( px[i][0] );
    eventos[i].evento:= StrToEvento( px[i][1] );
  end;
  for i:= 0 to high( px ) do
  begin
    setlength( px[0], 0 );
    setlength( px[1], 0 );
  end;
  setlength( px, 0 );
end;


procedure TReferenciaMonitor.notificarPosEnManejador(pos: integer);
begin
  posEnSala := pos;
end;

function TReferenciaMonitor.cosasMonitoreables(cosasConVariablesPublicadas:
  TListaDeCosasConNombre{of TReferenciaMonitor}): TListaDeCosasConNombre;
var
  i, j: integer;
  tieneTipoMonitoreable: boolean;
  cosa: TCosaConNombre;
begin
  //  resultado := cosasConVariablesPublicadas.Create_Clone as TListaDeCosasConNombre;

  //Saco a los monitores que me monitorean (y a mi mismo)
  cosasConVariablesPublicadas.Remove(self);
  for i := 0 to cosasConVariablesPublicadas.Count - 1 do
  begin
    tieneTipoMonitoreable := False;
    cosa := TCosaConNombre(cosasConVariablesPublicadas[i]);
    for j := 0 to cosa.pubvarlst.Count - 1 do
    begin
      if puedeMonitorearTipo(cosa.pubvarlst[j]) then
      begin
        tieneTipoMonitoreable := True;
        break;
      end;
    end;

    //Si no publica una variable que yo pueda monitorear o es un monitor que me
    //monitorea a mi lo saco de la lista de cosas que puedo monitorear
    if (not tieneTipoMonitoreable) or
      ((TCosaConNombre(cosasConVariablesPublicadas[i]) is TReferenciaMonitor) and
      (TReferenciaMonitor(cosasConVariablesPublicadas[i]).monitoreaA(self))) then
      cosasConVariablesPublicadas[i] := nil;
  end;
  cosasConVariablesPublicadas.Pack;
  Result := cosasConVariablesPublicadas;
end;

class function TReferenciaMonitor.cosasMonitoreablesDeClase(
  cosasConVariablesPublicadas: TListaDeCosasConNombre): TListaDeCosasConNombre;
var
  i, j: integer;
  tieneTipoMonitoreable: boolean;
  cosa: TCosaConNombre;
begin
  //  resultado := cosasConVariablesPublicadas.Create_Clone as TListaDeCosasConNombre;

  //Saco todas las cosas que no publican variables que yo pueda monitorear
  for i := 0 to cosasConVariablesPublicadas.Count - 1 do
  begin
    tieneTipoMonitoreable := False;
    cosa := TCosaConNombre(cosasConVariablesPublicadas[i]);
    for j := 0 to cosa.pubvarlst.Count - 1 do
    begin
      if puedeMonitorearTipo(cosa.pubvarlst[j]) then
      begin
        tieneTipoMonitoreable := True;
        break;
      end;
    end;
    if not tieneTipoMonitoreable then
      cosasConVariablesPublicadas[i] := nil;
  end;
  cosasConVariablesPublicadas.Pack;
  Result := cosasConVariablesPublicadas;
end;

class function TReferenciaMonitor.nombresVarsMonitoreables(cosa:
  TCosaConNombre): TStrings;
var
  i: integer;
  resultado: TStringList;
begin
  resultado := TStringList.Create;
  for i := 0 to cosa.pubvarlst.Count - 1 do
    if puedeMonitorearTipo(TVarDef(cosa.pubvarlst[i])) then
      resultado.Add(TVarDef(cosa.pubvarlst[i]).nombreVar);
  Result := resultado;
end;

function TReferenciaMonitor.esDeSimulacion: boolean;
var
  i: integer;
  resultado: boolean;
begin
  resultado := False;
  for  i := 0 to high(eventos) do
    if TipoDeEvento(eventos[i].evento) = 1 then
    begin
      resultado := True;
      break;
    end;
  Result := resultado;
end;

function TReferenciaMonitor.esDeOptimizacion: boolean;
var
  i: integer;
  resultado: boolean;
begin
  resultado := False;
  for  i := 0 to high(eventos) do
    if TipoDeEvento(eventos[i].evento) = 2 then
    begin
      resultado := True;
      break;
    end;
  Result := resultado;
end;

class function TReferenciaMonitor.puedeMonitorearTipo(varDef: TVarDef): boolean;
var
  i: integer;
  aux: AOfVarDef;
  resultado: boolean;
begin
  resultado := False;
  aux := Self.tiposMonitoreables;
  for i := 0 to high(aux) do
    if varDef is aux[i] then
    begin
      resultado := True;
      break;
    end;
  Result := resultado;
end;

procedure TReferenciaMonitor.Free;
begin
  if monitor <> nil then
    monitor.Free;
  SetLength(eventos, 0);
  inherited Free;
end;

//-------------------------------
//Metodos de TReferenciaMonUniVar
//===============================

constructor TReferenciaMonUniVar.Create( capa: integer; xnombreMon, xnombreCosa,
  xclaseCosa, xnombreVar: string;{ indice : Integer ; }xEventosProcs: TDAOfEventoProc);
begin
  inherited Create(capa, xnombreMon, xEventosProcs);
  defVar := TReferenciaDefVar.Create(capa, xnombreCosa, xclaseCosa, xnombreVar);
end;

function TReferenciaMonUniVar.Rec: TCosa_RecLnk;
begin
  Result:=inherited Rec;
  Result.addCampoDef( 'nombreCosa', px.nombreCosa, 0, 18 );
  Result.addCampoDef('claseCosa', px.claseCosa, 0, 18 );
  Result.addCampoDef('nombreVar', px.nombreVar, 0, 18 );
  Result.addCampoDef('defVar', TCosa(defVar), 18 );
end;

procedure TReferenciaMonUniVar.BeforeRead(version, id_hilo: integer);
begin
  inherited BeforeRead(version, id_hilo);
   px:= TReferenciaMonUniVar_auxRec.Create;
end;

procedure TReferenciaMonUniVar.AfterRead(f:TArchiTexto);
begin
  inherited AfterRead(f);
  if f.Version < 18 then
    defVar := TReferenciaDefVar.Create( capa, px.nombreCosa, px.claseCosa, px.nombreVar);
  px.Free;
end;


function TReferenciaMonUniVar.monitoreaA(cosa: TCosaConNombre): boolean;
begin
  Result := (defvar.nombreCosa = cosa.nombre) and (defvar.claseCosa = cosa.ClassName);
end;

function TReferenciaMonUniVar.notificarCambioCosa(cosaOrig, cosaNueva:
  TCosaConNombre): boolean;
begin
  if (defvar.nombreCosa = cosaOrig.nombre) and (defvar.claseCosa =
    cosaOrig.ClassName) then
  begin
    defvar.nombreCosa := cosaNueva.nombre;
    Result := True;
  end
  else
    Result := False;
end;

function TReferenciaMonUniVar.getReferenciasDefVars: TDAOfTReferenciaDefVar;
var
  res: TDAOfTReferenciaDefVar;
begin
  SetLength(res, 1);
  res[0] := defVar;
  Result := res;
end;

procedure TReferenciaMonUniVar.Free;
begin
  defVar.Free;
  inherited Free;
end;

//---------------------------------
//Metodos de TReferenciaMonMultiVar
//=================================










constructor TReferenciaMonMultiVar.Create(capa: integer;xnombreMon: string;
  xEventosProcs: TDAOfEventoProc; defVars: TListaDeCosas);
begin
  inherited Create( capa, xnombreMon, xEventosProcs);
  self.defVars := defVars;
end;

function TReferenciaMonMultiVar.Rec: TCosa_RecLnk;
begin
  Result:=inherited Rec;
  Result.addCampoDef('defVars', TCosa(defVars));
end;

procedure TReferenciaMonMultiVar.BeforeRead(version, id_hilo: integer);
begin
  inherited BeforeRead(version, id_hilo);
end;

procedure TReferenciaMonMultiVar.AfterRead(f:TArchiTexto);
begin
  inherited AfterRead(f);
end;


function TReferenciaMonMultiVar.monitoreaA(cosa: TCosaConNombre): boolean;
var
  i: integer;
  encontre: boolean;
begin
  encontre := False;
  for  i := 0 to defVars.Count - 1 do
    if (TReferenciaDefVar(defVars[i]).nombreCosa = cosa.nombre) and
      (TReferenciaDefVar(defVars[i]).claseCosa = cosa.ClassName) then
    begin
      encontre := True;
      break;
    end;
  Result := encontre;
end;

function TReferenciaMonMultiVar.notificarCambioCosa(cosaOrig, cosaNueva:
  TCosaConNombre): boolean;
var
  i: integer;
  res: boolean;
begin
  res := False;
  for i := 0 to defVars.Count - 1 do
    if (TReferenciaDefVar(defVars[i]).nombreCosa = cosaOrig.nombre) and
      (TReferenciaDefVar(defVars[i]).claseCosa = cosaOrig.ClassName) then
    begin
      TReferenciaDefVar(defVars[i]).nombreCosa := cosaNueva.nombre;
      res := True;
    end;
  Result := res;
end;

function TReferenciaMonMultiVar.getReferenciasDefVars: TDAOfTReferenciaDefVar;
begin
  Result := TDAOfTReferenciaDefVar(defVars.toArray);
end;

procedure TReferenciaMonMultiVar.Free;
begin
  defVars.Free;
  inherited Free;
end;

procedure AlInicio;
begin
  registrarClaseDeCosa(TReferenciaDefVar.ClassName, TReferenciaDefVar);
end;

procedure AlFinal;
begin
end;

end.
