{$DEFINE EMPAQUETAR_EVITAR_DUPLICADOS}
unit uempaquetar;

{$mode delphi}
interface

uses
  Classes, SysUtils,
  uInicioYFinal,
  zipper,
  uauxiliares,ucosanubeseable,
  uCosa, uCosaConNombre,
  uConstantesSimSEE,
  uSalasDeJuego;

type
  // la función debe retornar un strin vació si no se encontró.
  TSearchFileFunc = function(archi: string): string;


// crea una carpeta con el mismo nombre de la sala +'_empaquetada' y en
// esa carpeta copia la sala y todos los archivos necesarios para us ejecusión
// al copiar la sala cambia las referencias de los archivos a locales a la carpeta.
// retorna el camino completo de la carpeta.
function EmpaquetarSalaEnCarpeta(archi_sala: string;
  search_file_fun: TSearchFileFunc): string;

// crea un archivo con el misno nombre de la sala +'_empaquetada.zip' y copia
// en ese archivo la sala y los archivos necesarios para su ejecusión.
// al copiar la sala cambia las referencias a los archivos a locales a la carpeta
// para que funcione al descomprimir.
// Retorna el camino completo del archivo zip.
function EmpaquetarSalaEnZip(var sala: TSalaDeJuego;
  search_file_fun: TSearchFileFunc; var flgEliminaronArchs: boolean): string;


implementation


procedure adddrive(var archi: string; const drivesala: string);
begin
  if pos(driveSala, archi) = 0 then
  begin
    archi := driveSala + archi;
  end;
end;


function CopyFile(archivoOrigen, archivoDestino: string;
  const drivesala: string): boolean;
var
  f, fs: file;
  buff: array[0..102410 - 1] of byte;
  // ojo no pasarse de rosca que esto está en el stack.
  n: integer;
  res: integer;
  oldFileMode: integer;
begin
{$IFDEF WINDOWS}
  adddrive(archivoOrigen, drivesala);
  adddrive(archivoDestino, drivesala);
{$ENDIF}
  oldFileMode := filemode;
  filemode := 0; // readonl
  Assign(f, archivoOrigen);
  reset(f, 1);
  filemode := 1;
  Assign(fs, archivoDestino);
  rewrite(fs, 1);
  filemode := OldFileMode;
  n := 0;
  res := 0;
  repeat
    blockread(f, buff, 102410, n);
    blockwrite(fs, buff, n, res);
  until (n = 0) or (n <> res);
  Close(fs);
  Close(f);
  Result := n = res;
end;


function EmpaquetarSalaEnCarpeta(archi_sala: string;
  search_file_fun: TSearchFileFunc): string;
var
  f: TArchiTexto;
  sala: TSalaDeJuego;
  carpeta: string;
  nombreSala: string;
  ipos: integer;
  extSala, driveSala: string;

  rae: TArchiRef_Nubeseable;
  k: integer;
  r: string;
  ts: string;
  Catalogo: TCatalogoReferencias;
  lstaux: TListaDeArchiRef_Nubeseable;

begin
  carpeta := '';

  try

     uInicioYFinal.AlInicio;

    chdir(ExtractFilePath(archi_sala));
    Catalogo:= TCatalogoReferencias.Create;

    f := TArchiTexto.CreateForRead(0, Catalogo, archi_sala, False);
    f.rd('sala', TCosa(sala));
    sala.setDirCorrida(archi_sala);
    f.Free;

    // si es necesaio guardo la lista de referencias.
    if sala.archs <> nil then
    begin
      lstaux := sala.archs ;
      //ReferenciasAArchivosNubeseados := TList.Create;
    end
    else
      lstaux := nil;

    Catalogo.resolver_referencias(sala.listaActores);
    if Catalogo.resolver_referencias(sala.listaFuentes_) > 0 then
    begin
      Catalogo.DumpReferencias(getDir_Dbg + 'Err_refs.txt');
      raise Exception.Create('ERROR, quedan referencias sin resolver.');
    end;
    if Catalogo.resolver_referenciasDeArch(sala.archs) > 0 then
    begin
      Catalogo.DumpReferencias(getDir_Dbg + 'Err_refs.txt');
      raise Exception.Create('ERROR, quedan referencias a archivos sin resolver.');
    end;
    Catalogo.Free;

    nombreSala := ExtractFileName(archi_sala);
    extSala := ExtractFileExt(archi_sala);
    {$IFDEF WINDOWS}
    driveSala := ExtractFileDrive(archi_sala);
    {$ELSE}
    driveSala := '';
    {$ENDIF}
    ipos := pos('.', nombreSala);
    if ipos > 0 then
      Delete(nombreSala, ipos, length(nombreSala) - ipos + 1);

    carpeta := ExtractFilePath(archi_sala) + nombreSala + '_empaquetada';
    if not DirectoryExists(carpeta) then
      createdir(carpeta);


    for k := 0 to sala.archs.Count - 1 do
    begin
      rae := sala.archs.items[k];
      if rae.flg_nubeseable then
        if rae.ArchiRef.archi <> '' then
        begin
          if (not FileExists(rae.ArchiRef.archi)) then
          begin
            if @search_file_fun <> nil then
              ts := search_file_fun(rae.ArchiRef.archi)
            else
              ts := '';

            if ts = '' then
              raise Exception.Create('Archivo no encontrado: ' + rae.ArchiRef.archi);
            rae.ArchiRef.archi := ts;
          end;
          r := ExtractFileName(rae.ArchiRef.archi);
          CopyFile(rae.ArchiRef.archi, carpeta + DirectorySeparator + r, driveSala);
          rae.ArchiRef.archi := r;
        end;
    end;
    sala.WriteToArchi(carpeta + DirectorySeparator + nombreSala + extSala);
    sala.Free;
  finally

    uInicioYFinal.AlFinal;
    if lstaux <> nil then
      sala.archs := lstaux;

  end;
  Result := carpeta;
end;



function EmpaquetarSalaEnZip(var sala: TSalaDeJuego;
  search_file_fun: TSearchFileFunc; var flgEliminaronArchs: boolean): string;
var
  f: TArchiTexto;
  salaAux: TSalaDeJuego;
  existe_ref: boolean;
  nombreSala, nombreSala_completo: string;
  archiMon: string;
  ipos: integer;
  extSala, driveSala: string;
  zipname: string;
  fzip: TZipper;
  rae: TArchiRef_Nubeseable;
  k,cant: integer;
  r: string;
  ts: string;

  lstaux: TListaDeArchiRef_Nubeseable;
  Catalogo: TCatalogoReferencias;

  {$IFDEF EMPAQUETAR_EVITAR_DUPLICADOS}
  lstnombres: TStringList;
  lstnombres_origen: TStringList;
  r_origen: string;
  cntrep: integer;
  klocate: integer;
  buscando_rep: boolean;
  flg_skip: boolean;
  {$ENDIF}

begin
  zipname := '';
  nombreSala_completo := sala.archiSala_;
  nombreSala := ExtractFileName(sala.archiSala_);
  extSala := ExtractFileExt(sala.archiSala_);
  {$IFDEF WINDOWS}
  driveSala := ExtractFileDrive(sala.archiSala_);
  {$ELSE}
  driveSala := '';
  {$ENDIF}
  ipos := pos('.', nombreSala);
  if ipos > 0 then
    Delete(nombreSala, ipos, length(nombreSala) - ipos + 1);

  try
    {$IFDEF EMPAQUETAR_EVITAR_DUPLICADOS}
    lstnombres := TStringList.Create;
    lstnombres.Sorted:= true;
    lstnombres_origen := TStringList.Create;
    {$ENDIF}

    uInicioYFinal.AlInicio;

    Catalogo:= TCatalogoReferencias.Create;
    chdir(ExtractFilePath(sala.archiSala_));
    f := TArchiTexto.CreateForRead(0, Catalogo, sala.archiSala_, False);
    f.rd('sala', TCosa(sala));
    sala.setDirCorrida(nombreSala_completo);
    f.Free;

    // si es necesaio guardo la lista de referencias.
    if sala.archs <> nil then
    begin
      lstaux := sala.archs ;
      //ReferenciasAArchivosNubeseados := TList.Create;
    end
    else
      lstaux := nil;

    Catalogo.resolver_referencias(sala.listaActores);
    Catalogo.resolver_referencias(sala.listaCombustibles);
    Catalogo.resolver_referencias(sala.listaFuentes_);
    if Catalogo.resolver_referenciasDeArch(sala.archs) > 0 then
    begin
      Catalogo.DumpReferencias(getDir_Dbg + 'Err_refs.txt');
      raise Exception.Create('ERROR, quedan referencias  sin resolver.');
    end;





    fzip := TZipper.Create;

    zipname := ExtractFilePath(sala.archiSala_) + nombreSala + '.zip';
    fzip.FileName := zipname;
    if FileExists(fzip.FileName) then
      deletefile(fzip.FileName);

    archiMon := ExtractFilePath(sala.archiSala_) + nombreSala + '.mon';
    if fileExists(archiMon) then
    begin
      r := ExtractFileName(archiMon);
      fzip.Entries.AddFileEntry(archiMon, r);
    end;

    k:=sala.archs.Count;


    for k := 0 to sala.archs.Count - 1 do
    begin
      rae := sala.archs.items[k];
      if not rae.flg_nubeseable then
        if rae.ArchiRef.archi <> '' then
        begin
          if (not FileExists(rae.ArchiRef.archi)) then
          begin
            if @search_file_fun <> nil then
              ts := search_file_fun(rae.ArchiRef.archi)
            else
              ts := '';

            if ts = '' then
            begin
              assert(Catalogo.referenciasSinResolver = 0,
              'usalasdejuegoParaEditor.existeReferenciaALaCosaConNombre: quedan referencias sin resolver en la sala');
              salaAux := TSalaDeJuego(sala.Create_Clone(Catalogo, 0));
              existe_ref := Catalogo.existeReferencia_alArchi(rae);
              salaAux.Free;
              Catalogo.LimpiarReferencias;
              if existe_ref then
                 raise Exception.Create('Archivo no encontrado: ' + rae.ArchiRef.archi +
                 ' ,existe una referencia a él')
              else
              begin
                 sala.archs.Remove(sala.archs[k]); // si no encuentra el archi en directorios y en la sala no se referencia, se elimina el archivo de la sala
                  flgEliminaronArchs:=true;
                 Break;
              end;
            end
            else
                rae.ArchiRef.archi := ts;
          end;
          //        r:= nombreSala+'_z'+IntToStr( k )+'_'+ExtractFileName( rae.archiRefStr );
          r := ExtractFileName(rae.ArchiRef.archi);
  {$IFDEF EMPAQUETAR_EVITAR_DUPLICADOS}
          flg_skip := False;
          if lstnombres.Find(r, klocate) then
          begin
            if (lstnombres_origen[klocate] <> rae.ArchiRef.archi) then
            begin
              cntrep := 1;
              buscando_rep := True;
              while buscando_rep do
              begin
                if lstnombres.Find('r' + IntToStr(cntrep) + '_' + r, klocate) then
                  if (lstnombres_origen[klocate] <> rae.ArchiRef.archi) then
                    Inc(cntrep)
                  else
                  begin
                    buscando_rep := False;
                    flg_skip := True;
                  end
                else
                  buscando_rep := False;
              end;
              r := 'r' + IntToStr(cntrep) + '_' + r;
            end
            else
              flg_skip := True;
          end;

          if not flg_skip then
          begin
            r_origen := rae.ArchiRef.archi;
            fzip.Entries.AddFileEntry(rae.ArchiRef.archi, r);
            rae.ArchiRef.archi := r;
            lstnombres.Add(rae.ArchiRef.archi);
            lstnombres_origen.add(r_origen);
          end
          else
            rae.ArchiRef.archi := r;
  {$ELSE}
          fzip.Entries.AddFileEntry(rae.ArchiRef.archi, r);
          rae.ArchiRef.archi := r;
  {$ENDIF}
        end;
    end;

    sala.WriteToArchi('_sala_tmp_.ese');
    fzip.Entries.AddFileEntry('_sala_tmp_.ese', nombreSala + extSala);
    //sala.Free;
    fzip.ZipAllFiles;
    deletefile('_sala_tmp_.ese');
    fzip.Free;
  finally
    {$IFDEF EMPAQUETAR_EVITAR_DUPLICADOS}
    lstnombres.Free;
    lstnombres_origen.Free;
    {$ENDIF}
    uInicioYFinal.AlFinal;
    if lstaux <> nil then
      sala.archs := lstaux;
  end;
  Catalogo.Free;
  Result := zipname;
end;

end.
