unit cmpTabbedFilesMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Math;

const
  unSegundo = 1 / 24 / 3600; 

type
  TDif = class
    fila, col, tab: Integer;

    Constructor Create(fila, col, tab: Integer);
  end;

  TcmpTabbedFiles = class(TForm)
    EArch1: TEdit;
    EArch2: TEdit;
    LArch1: TLabel;
    LArch2: TLabel;
    BBuscar1: TButton;
    BBuscar2: TButton;
    BComparar: TButton;
    OpenDialog1: TOpenDialog;
    MDifs: TMemo;
    procedure BBuscar1Click(Sender: TObject);
    procedure BBuscar2Click(Sender: TObject);
    procedure BCompararClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    //indica si se debe cerrar el programa cuando dos archivos sean iguales
    closeOnOk: boolean;

    //retorna true si las 2 lineas son iguales
    //sino retorna false y colDiferencia y colTab tienen la columna y columna tabulada
    //en las que se encontro diferencia
    function cmpString(const linea1, linea2 : String; var colDiferencia, colTab : Integer) : boolean;
    //retorna true si los 2 archivos son iguales
    function comparar(arch1, arch2: String) : boolean;
    function nroColToExcelStrCol(nroCol: Integer) : String;
    function potencia(base, exponente : Integer) : Integer;
    //Busca en lugares predefinidos archivos para comparar contra el especificado
    function intentarEncontrarOriginal(arch1: String): String;
  public
    { Public declarations }
  end;

var
  cmpTabbedFiles: TcmpTabbedFiles;

implementation

uses uconstantesSimSEE;

{$R *.dfm}

Constructor TDif.Create(fila, col, tab: Integer);
begin
  inherited Create();
  self.fila:= fila;
  self.col:= col;
  self.tab:= tab;
end;

procedure TcmpTabbedFiles.BBuscar1Click(Sender: TObject);
begin
  if OpenDialog1.Execute then
  begin
    EArch1.Text:= OpenDialog1.FileName;
    EArch2.Text:= intentarEncontrarOriginal(EArch1.Text);
  end;
end;

procedure TcmpTabbedFiles.BBuscar2Click(Sender: TObject);
begin
  if OpenDialog1.Execute then
    EArch2.Text:= OpenDialog1.FileName;
end;

procedure TcmpTabbedFiles.BCompararClick(Sender: TObject);
begin
  BBuscar1.Enabled:= false;
  BBuscar2.Enabled:= false;
  BComparar.Enabled:= false;
  if FileExists(EArch1.Text) and FileExists(EArch2.text) then
  begin
    if comparar(EArch1.Text, EArch2.Text) then
    begin
      ShowMessage('Los Archivos Son Iguales');
      if closeOnOk then
        PostQuitMessage(0);
    end
    else
    begin
      ShowMessage('Se encontraron diferencias en los archivos');
    end;
  end
  else
    if not FileExists(EArch1.Text) then
      ShowMessage('El archivo ' + EArch1.Text + #13 + ' (Archivo1) no existe!')
    else
      ShowMessage('El archivo ' + EArch2.Text + #13 + ' (Archivo2) no existe!');
  BBuscar1.Enabled:= true;
  BBuscar2.Enabled:= true;
  BComparar.Enabled:= true;
end;

function TcmpTabbedFiles.intentarEncontrarOriginal(arch1: String): String;
var
  path, ext: String;
begin
  path:= ExtractFilePath(arch1);
  path:= path + 'originales\' + ExtractFileName(arch1);
  if FileExists(path) then
    result:= path
  else
  begin
    ext:= ExtractFileExt(path);
    path:= copy(path, 0, Length(path) - length(ext)) + '_' + ext;
    if FileExists(path) then
      result:= path
    else
      result:= '';
  end;
end;

function TcmpTabbedFiles.cmpString(const linea1, linea2 : String; var colDiferencia, colTab : Integer) : boolean;
var
  i: Integer;
  encontre: boolean;
begin
  colDiferencia:= -1;
  i:= 1;
  encontre:= false;
  colTab:= 1;
  while ((i <= Length(linea1)) and (i <= Length(linea2)) and not encontre) do
  begin
    if linea1[i] <> linea2[i] then
    begin
      colDiferencia:= i;
      encontre:= true;
    end;
    if linea1[i] = #9 then
      inc(colTab);
    inc(i);     
  end;

  if encontre then
  begin
    result:= false;
  end
  else
  begin
    if i = Length(linea1) + 1 then
    begin
      if i = Length(linea2) + 1 then
        result:= colDiferencia = -1
      else
      begin
        colDiferencia:= Length(linea1) + 1;
        result:= false;
      end;
    end
    else //if i = Length(Str2) then
    begin
      colDiferencia:= Length(linea2)  + 1;
      result:= false;
    end
  end;
end;

function TcmpTabbedFiles.comparar(arch1, arch2: String) : boolean;
var
  f1, f2: TextFile;
  difEnLinea1, difEnLinea2: String;
  linea1, linea2: String;
  largoOrigLinea1, largoOrigLinea2: Integer;
  iLinea, iCol, iColPrincipioPalabra, iTab, sumICol, sumITab: Integer;
  lineasIguales, archsIguales: boolean;
  posPrincipioPalabra, posFinPalabra: Integer;
  t: TDateTime;
begin
  mdifs.Clear;
  MDifs.Lines.Add('Diferencias:');
  archsIguales:= false;
  try
    AssignFile(f1, arch1);
    Reset(f1);
    AssignFile(f2, arch2);
    Reset(f2);

    archsIguales:= true;
    iLinea:= 1;
    t:= now;
    while not Eof(f1) and not Eof(f2) do
    begin
      Readln(f1, linea1);
      Readln(f2, linea2);
      sumICol:= 0;
      sumITab:= 0;
      repeat
        if iLinea = 14 then
          iLinea:= iLinea;
        lineasIguales:= cmpString(linea1, linea2, iCol, iTab);
        if not lineasIguales then
        begin
          largoOrigLinea1:= Length(linea1);

          //Busco el principio de la palabra
          if iCol > Length(linea1) then
          begin
            difEnLinea1:= '';
            linea1:= '';
          end
          else
          begin
            iColPrincipioPalabra:= iCol;

            if iColPrincipioPalabra > Length(linea2) then
            begin
              posPrincipioPalabra:= iCol;
              while (posPrincipioPalabra >= 2) and (linea1[posPrincipioPalabra] <> #9) do
                Dec(posPrincipioPalabra);

              difEnLinea1:= copy(linea1, posPrincipioPalabra, Length(linea1) - posPrincipioPalabra);
              linea1:= '';
            end
            else
            begin
              posPrincipioPalabra:= iCol;
              while (posPrincipioPalabra >= 2) and (linea1[posPrincipioPalabra - 1] <> #9) do
                Dec(posPrincipioPalabra);

              posFinPalabra:= iCol;
              while (posFinPalabra < Length(linea1)) and (linea1[posFinPalabra + 1] <> #9) do
                Inc(posFinPalabra);

              difEnLinea1:= copy(linea1, posPrincipioPalabra, posFinPalabra - posPrincipioPalabra + 1);
              if (posFinPalabra + 1) < length(linea1) then
                linea1:= Copy(linea1, posFinPalabra + 2, Length(linea1) - (posFinPalabra + 2))
              else
                linea1:= '';
            end;
          end;

          if iCol > Length(linea2) then
          begin
            difEnLinea2:= '';
            linea2:= '';
          end
          else
          begin
            //Busco el principio de la palabra
            iColPrincipioPalabra:= iCol;

            if iColPrincipioPalabra > largoOrigLinea1 then
            begin
              posPrincipioPalabra:= iCol;
              while (posPrincipioPalabra >= 2) and (linea2[posPrincipioPalabra] <> #9) do
                Dec(posPrincipioPalabra);

              difEnLinea2:= copy(linea2, posPrincipioPalabra, Length(linea2) - posPrincipioPalabra);
              linea2:= '';
            end
            else
            begin
              posPrincipioPalabra:= iCol;
              while (posPrincipioPalabra >= 2) and (linea2[posPrincipioPalabra - 1] <> #9) do
                Dec(posPrincipioPalabra);

              posFinPalabra:= iCol;
              while (posFinPalabra < Length(linea2)) and (linea2[posFinPalabra + 1] <> #9) do
                Inc(posFinPalabra);

              difEnLinea2:= copy(linea2, posPrincipioPalabra, posFinPalabra - posPrincipioPalabra + 1);
              if (posFinPalabra + 1) < length(linea2) then
                linea2:= Copy(linea2, posFinPalabra + 2, Length(linea2) - (posFinPalabra + 2))
              else
                linea2:= '';
            end;
          end;

          inc(sumICol, iCol);
          inc(sumITab, iTab);
          //res.Add(TDif.Create(iLinea, sumICol, sumITab));
          MDifs.Lines.Add('Dif fila:' + IntToStr(iLinea) +
                          ', col:' + IntToStr(sumICol) +
                          ', colTabulada: ' + nroColToExcelStrCol(sumITab) +
                          ', (' + difEnLinea1 + ')->(' + difEnLinea2 + ')');
          archsIguales:= false;
          self.Show;                          
        end;
      until ((linea1 = '') or (linea2 = '')) or lineasIguales;

      if length(linea1) <> Length(linea2) then
      begin
        MDifs.Lines.Add('Dif fila:' + IntToStr(iLinea) +
                        ', col:' + IntToStr(sumICol) +
                        ', colTabulada: ' + nroColToExcelStrCol(sumITab) +
                        ', (' + linea1 + ')->(' + linea2 + ')');
        archsIguales:= false;
        self.Show;
      end;

      if now() - t > unSegundo * 0.2 then
      begin
        Application.ProcessMessages;
        t:= now();
      end;
      inc(iLinea);
    end;
  finally
    CloseFile(f1);
    CloseFile(f2);
    result:= archsIguales;
  end;
end;

function TcmpTabbedFiles.nroColToExcelStrCol(nroCol: Integer) : String;
const
  ExcelCols :array[1..256] of string =
              ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
               'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
               'AA', 'AB', 'AC', 'AD', 'AE', 'AF', 'AG', 'AH', 'AI', 'AJ', 'AK', 'AL', 'AM',
               'AN', 'AO', 'AP', 'AQ', 'AR', 'AS', 'AT', 'AU', 'AV', 'AW', 'AX', 'AY', 'AZ',
               'BA', 'BB', 'BC', 'BD', 'BE', 'BF', 'BG', 'BH', 'BI', 'BJ', 'BK', 'BL', 'BM',
               'BN', 'BO', 'BP', 'BQ', 'BR', 'BS', 'BT', 'BU', 'BV', 'BW', 'BX', 'BY', 'BZ',
               'CA', 'CB', 'CC', 'CD', 'CE', 'CF', 'CG', 'CH', 'CI', 'CJ', 'CK', 'CL', 'CM',
               'CN', 'CO', 'CP', 'CQ', 'CR', 'CS', 'CT', 'CU', 'CV', 'CW', 'CX', 'CY', 'CZ',
               'DA', 'DB', 'DC', 'DD', 'DE', 'DF', 'DG', 'DH', 'DI', 'DJ', 'DK', 'DL', 'DM',
               'DN', 'DO', 'DP', 'DQ', 'DR', 'DS', 'DT', 'DU', 'DV', 'DW', 'DX', 'DY', 'DZ',
               'EA', 'EB', 'EC', 'ED', 'EE', 'EF', 'EG', 'EH', 'EI', 'EJ', 'EK', 'EL', 'EM',
               'EN', 'EO', 'EP', 'EQ', 'ER', 'ES', 'ET', 'EU', 'EV', 'EW', 'EX', 'EY', 'EZ',
               'FA', 'FB', 'FC', 'FD', 'FE', 'FF', 'FG', 'FH', 'FI', 'FJ', 'FK', 'FL', 'FM',
               'FN', 'FO', 'FP', 'FQ', 'FR', 'FS', 'FT', 'FU', 'FV', 'FW', 'FX', 'FY', 'FZ',
               'GA', 'GB', 'GC', 'GD', 'GE', 'GF', 'GG', 'GH', 'GI', 'GJ', 'GK', 'GL', 'GM',
               'GN', 'GO', 'GP', 'GQ', 'GR', 'GS', 'GT', 'GU', 'GV', 'GW', 'GX', 'GY', 'GZ',
               'HA', 'HB', 'HC', 'HD', 'HE', 'HF', 'HG', 'HH', 'HI', 'HJ', 'HK', 'HL', 'HM',
               'HN', 'HO', 'HP', 'HQ', 'HR', 'HS', 'HT', 'HU', 'HV', 'HW', 'HX', 'HY', 'HZ',
               'IA', 'IB', 'IC', 'ID', 'IE', 'IF', 'IG', 'IH', 'II', 'IJ', 'IK', 'IL', 'IM',
               'IN', 'IO', 'IP', 'IQ', 'IR', 'IS', 'IT', 'IU', 'IV');
begin
  Result:= ExcelCols[nroCol];
end;

function TcmpTabbedFiles.potencia(base, exponente : Integer) : Integer;
var
  res: Integer;
begin
  if exponente = 0 then
  begin
    result:= 1;
  end
  else if exponente = 1 then
  begin
    result:= base
  end
  else
  begin
    res:= potencia(base, exponente div 2);
    if exponente mod 2 = 0 then
      result:= res * res
    else
      result:= res * res * base;
  end;
end;

procedure TcmpTabbedFiles.FormCreate(Sender: TObject);
begin
  if ParamCount > 0 then
  begin
    closeOnOk:= true;
    EArch1.Text:= ParamStr(1);
    OpenDialog1.InitialDir:= ExtractFilePath(EArch1.Text);
    if ParamCount > 1 then
    begin
      EArch2.Text:= ParamStr(2);
      BCompararClick(self);
    end
    else
    begin
      EArch2.Text:= intentarEncontrarOriginal(EArch1.Text);
      if EArch2.Text <> '' then
        BCompararClick(self);
    end;
  end
  else
  begin
    OpenDialog1.InitialDir:= uConstantesSimSEE.getDir_Run;
    closeOnOk:= false;
  end;
end;

end.
