create or replace package xrp is

   function val(x in varchar2) return varchar2;
   function val(x in date) return varchar2;
   function val(x in number) return varchar2;
   Procedure IniArchivo(pArchivo in Varchar2, pLinea in Number);
   Procedure RegSalida(pArchivo in Varchar2, pTexto in Varchar2);

end;
/

create or replace package body xrp is

   Type TabLineas is
      Record (linea pls_integer);

   Type TabArchivos is
      Table of TabLineas index by varchar2(30);

   vArchivos TabArchivos;

   function val(x in varchar2) return varchar2 is
   begin
      if x is null then
         return 'null,';
      else
         return ''''||Replace(x, '''', '''''')||''',';
      end if;
   end;
   function val(x in date) return varchar2 is
   begin
      if x is null then
         return 'null,';
      else
         return 'to_date('''||to_char(x,'ddmmyyyy hh24miss')||''',''ddmmyyyy hh24miss''),';
      end if;
   end;
   function val(x in number) return varchar2 is
   begin
      if x is null then
         return 'null,';
      else
         return Replace(x, ',', '.')||',';
      end if;
   end;

   Procedure IniArchivo(pArchivo in Varchar2, pLinea in Number) is
   Begin
      vArchivos(pArchivo).Linea := pLinea;

      Delete From GenSalida
      Where Archivo = pArchivo;
   End IniArchivo;

   Function ObtLinea(pArchivo in Varchar2) Return Number Is
   Begin
      vArchivos(pArchivo).Linea := vArchivos(pArchivo).Linea+1;
      Return vArchivos(pArchivo).Linea-1;
   Exception
      When No_Data_Found Then
         vArchivos(pArchivo).Linea := 2;
         Return 1;
   End ObtLinea;

   Procedure RegSalida(pArchivo in Varchar2, pTexto in Varchar2) Is
      vPos    Number(4) := 0;
      vSep    Varchar2(1) := Chr(10);
      vIni    Number(4) := 1;
      vLinea  Number(5);
      vSalida Varchar2(240);
   Begin 
      Loop
         vIni := vPos+1;
         vPos := Instr(pTexto, vSep, vIni);

         If vPos = 0 Then
            vPos:=length(pTexto)+1;
         End If;

         vLinea  := ObtLinea(pArchivo);
         vSalida := Nvl(substr(ptexto, vIni, vPos - vIni), ' ');

         Insert Into GenSalida(Archivo, Linea, Texto)
                Values(pArchivo, vLinea, vSalida);

         Exit when vPos>=Length(pTexto);
      End Loop;
   End RegSalida;

end;
/

create or replace package replicacion is

   Type RecSrv is
      Record (Estado    varchar2(30),
              UltimaVer timestamp);
   Type TabSrv is
      Table of RecSrv index by varchar2(30);

   Procedure FijarEstado(pServidor in varchar2, pEstado in varchar2);
   function EstadoServidor(pServidor in varchar2) return varchar2;
   function LapsoServidor(pServidor in varchar2) return varchar2;
   function ChequearServidor(pServidor in varchar2) return varchar2;
   procedure RevisarServidores(pInstancia in varchar2, pEnLinea in boolean default false);
   Procedure Limpiar;

   function Actual(pNombre in varchar2) return number;
   function Siguiente(pNombre in varchar2) return number;
   procedure insertarCom(pServidor in varchar2, pInstancia in varchar2, pRutina in varchar2,
                         pTabla in varchar2, pTexto in varchar2);
   procedure insertarCom(pRutina in varchar2, pTabla in varchar2, pTexto in varchar2);
   procedure EjecutarCom(pServidor in varchar2, pInstancia in varchar2, pRutina in varchar2,
                         pTabla in varchar2, pTexto in varchar2, pEnLinea in boolean default false);
   procedure EjecutarCom(pRutina in varchar2, pTabla in varchar2, pTexto in varchar2,
                         pEnLinea in boolean default false);
   procedure insertarErr(pServidor in varchar2, pRutina in varchar2, pTexto in varchar2,
                         pMensaje in varchar2);
   function replicarSig(pInstancia in varchar2) return number;

end;
/

create or replace package body replicacion is

   vSrvs         TabSrv;
   vLapsoChequeo interval day to second:=interval '0 0:1:0' day to second;

   Procedure FijarEstado(pServidor in varchar2, pEstado in varchar2) is
   begin
      vSrvs(pServidor).Estado:=pEstado;
      vSrvs(pServidor).UltimaVer:=localtimestamp;
   end FijarEstado;

   function EstadoServidor(pServidor in varchar2) return varchar2 is
      vEstado varchar2(30);
   begin
      vEstado:=vSrvs(pServidor).Estado;
      if vEstado!='EnLinea' and (LocalTimeStamp-vSrvs(pServidor).UltimaVer)>vLapsoChequeo then
         vEstado:=ChequearServidor(pServidor);
         FijarEstado(pServidor, vEstado);
      end if;
      return vEstado;
   Exception
      when no_data_found then
         return '*Error*';
   end EstadoServidor;

   function LapsoServidor(pServidor in varchar2) return varchar2 is
   begin
      return vSrvs(pServidor).estado||' '||to_char(LocalTimeStamp-vSrvs(pServidor).ultimaVer,'dddd hh:mi:ss');
   Exception
      when no_data_found then
         return '*Error*';
   end LapsoServidor;

   function ChequearServidor(pServidor in varchar2) return varchar2 IS
      vX number(2);
   BEGIN
      EXECUTE IMMEDIATE 'Select 1 from dual@'||pServidor into vX;
      return 'EnLinea';
   Exception
      When others then
         return 'SinServ';
   END ChequearServidor;

   procedure RevisarServidores(pInstancia in varchar2, pEnLinea in boolean default false) is
      cursor servidores is
         select distinct servidor 
         from repinstancias
         where instancia=pInstancia;
   begin
      for s in servidores
      loop
         if vSrvs(s.Servidor).estado!='EnLinea' or pEnLinea then
            FijarEstado(s.Servidor, ChequearServidor(s.Servidor));
         end if;
      end loop;
   End RevisarServidores;

   Procedure Limpiar is
   begin
      vSrvs.Delete;
   end Limpiar;

   function Actual(pNombre in varchar2) return number is
      vConsec number(10);
   begin
      select valor into vConsec
      from repsecuencia
      where nombre=pNombre;
      return vConsec;
   exception
      when no_data_found then
         return 0;
   end;

   function Siguiente(pNombre in varchar2) return number is
      pragma autonomous_transaction;
      vConsec number(10);
   begin
      update repsecuencia
         set valor=valor+1
      where nombre=pNombre
      returning valor-1 into vConsec;
      commit;
      return vConsec;
   end;

   procedure insertarCom(pServidor in varchar2, pInstancia in varchar2, pRutina in varchar2,
                         pTabla in varchar2, pTexto in varchar2) is
      Vsec number(10):=Siguiente('REP');
   begin
      execute immediate 'insert into repcom'||pInstancia||
                         '   (secuencia, servidor, rutina, texto)'||
                         '   values (:s1,:s2, :s3, :s4)'
          using vSec, pServidor, pRutina||pTabla, substr(pTexto,1,length(pTexto)-1);
   end;

   procedure InsertarCom(pRutina in varchar2, pTabla in varchar2, pTexto in varchar2) is
      cursor rutinas is
          select servidor, pRutina rutina, instancia
          from repinstancias
          where tabla=pTabla
            and estado In ('A', 'C');
   begin
      for cmds in rutinas
      loop
         InsertarCom(cmds.servidor, cmds.instancia, cmds.rutina, pTabla, pTexto);
      end loop;
   end;

   Function EstadoInstancia(pServidor in Varchar2, pTabla in Varchar2) Return Varchar2 Is
      vEstado	RepInstancias.Estado%Type;
   Begin
      Select Estado
        Into vEstado
      From RepInstancias
      Where Servidor = pServidor
        And Tabla    = pTabla;

      Return vEstado;
   Exception
      When No_Data_Found Then
         Return 'I';
   End EstadoInstancia;

   procedure EjecutarCom(pServidor in varchar2, pInstancia in varchar2, pRutina in varchar2,
                         pTabla in varchar2, pTexto in varchar2, pEnLinea in boolean default false) is
      vEje         varchar2(1):='N';
      vNuevoEstado varchar2(30);
      vEstado      RepInstancias.Estado%Type;
   begin
      vEstado := EstadoInstancia(pServidor, pTabla);

      If vEstado In ('A', 'C') Then

         if vEstado = 'A' And (EstadoServidor(pServidor)='EnLinea' or pEnLinea) then
            begin
               execute immediate 'begin '||pRutina||pTabla||'@'||pServidor||'('||substr(pTexto,1,length(pTexto)-1)||'); end;';
               vEje:='S';
            exception
               when others then
                  vNuevoEstado:=ChequearServidor(pServidor);
                  if vNuevoEstado='EnLinea' or pEnLinea then
                     Raise;
                  elsif vNuevoEstado!='EnLinea' then
                     FijarEstado(pServidor, vNuevoEstado);
                  end if;
            end;
         end if;

         if vEje='N'then
            insertarCom(pServidor, pInstancia, pRutina, pTabla, pTexto);
         end if;
      End If;
   end;

   procedure EjecutarCom(pRutina in varchar2, pTabla in varchar2, pTexto in varchar2,
                         pEnlinea in boolean default false) is
      cursor rutinas is
          select servidor, pRutina rutina, instancia
          from repinstancias
          where tabla=pTabla
            and estado In ('A', 'C');
   begin
      for cmds in rutinas
      loop
         EjecutarCom(cmds.servidor, cmds.instancia, cmds.rutina, pTabla, pTexto, pEnLinea);
      end loop;
   end;

   procedure insertarErr(pServidor in varchar2, pRutina in varchar2, pTexto in varchar2,
                         pMensaje in varchar2) is
      Vsec number(10):=Siguiente('ERR');
   begin
      insert into reperrores (secuencia, servidor, rutina, texto, mensaje)
          values(vSec, pServidor, pRutina, pTexto, pMensaje);
   end;

   function replicarSig(pInstancia in varchar2) return number is
      vServidor  repcomdef.servidor%type;
      vRutina    repcomdef.rutina%type;
      vTexto     repcomdef.texto%type;
      vConsec    number(10);
   begin
      execute immediate
         'delete from repcom'||pInstancia||' '||
         'where   secuencia=(select min(secuencia) from repcom'||pInstancia||' '||
                            'where replicacion.estadoservidor(Servidor)=''EnLinea'') '||
         'returning secuencia, servidor, rutina, texto into :s1,:s2,:s3,:s4'
         using   out VConsec, out vServidor, out vRutina, out vTexto;

      if vConsec is not null then
         begin
            execute immediate 'begin '||vrutina||'@'||vServidor||'('||vtexto||'); end;';
         exception
            when others then
               insertarErr(vServidor, vRutina, vTexto, substr(sqlerrm,1,400));
               FijarEstado(vServidor, ChequearServidor(vServidor));
         end;
         commit;
      end if;
      return vConsec;
   end;

begin
   --for serv in (select distinct servidor from repinstancias)
   for serv in (select servidor from repservidor)
   loop
      FijarEstado(serv.Servidor, ChequearServidor(serv.servidor));
   end loop;
end;
/

create or replace package repsync is 

   procedure Replicar(pIns in varchar2, pMod in number);

end;
/

create or replace package body repsync is

   procedure Replicar(pIns in varchar2, pMod in number) is
      vConsec number(10);
      Vstatus pls_integer;
      Vmens   varchar2(40);
      VTrans  pls_integer:=0;
   begin
      loop
         vConsec:=Replicacion.ReplicarSig(pIns);
         vTrans:=Vtrans+1;
         if vConsec is null then
            dbms_alert.waitone('REPXXXNO', Vmens, vStatus, 5);
         end if;
         if vTrans>=pMod then
            vTrans:=0;
            VConsec:=Replicacion.Actual(pIns);
            exit when vConsec>0;
         end if;
      end loop;
   end;

end;
/

