unit udscanner_cleanconditionaldefine;

{$mode objfpc}{$H+}

interface

uses
  Classes, xmatdefs, SysUtils, Math, udirectoryscanner;

type

  { TDirectoryScanner_cleanconditionaldefine }

  TDirectoryScanner_cleanconditionaldefine = class(TDirectoryScanner)
    conditional_define: string;
    constructor Create(xconditional_define: string);
    procedure scan(const carpeta_raiz: string); override;
    procedure procesar_archivo(const carpeta, nombre_archivo: string); override;
  end;


implementation

const
  s_Apertura = '{';
  s_Cierre = '}';
  s_IFDEF = s_Apertura + '$IFDEF ';
  s_IFNDEF = s_Apertura + '$IFNDEF ';
  s_ELSE = s_Apertura + '$ELSE';
  s_ENDIF = s_Apertura + '$ENDIF';


{ TDirectoryScanner_cleanconditionaldefine }

constructor TDirectoryScanner_cleanconditionaldefine.Create(
  xconditional_define: string);
begin
  inherited Create;
  conditional_define := xconditional_define;
  add_ignore_carpeta('backup');
end;

procedure TDirectoryScanner_cleanconditionaldefine.scan(const carpeta_raiz: string);
begin
  procesar_carpeta(carpeta_raiz, '*.lpr');
  procesar_carpeta(carpeta_raiz, '*.dpr');
  procesar_carpeta(carpeta_raiz, '*.pp');
  procesar_carpeta(carpeta_raiz, '*.pas');
end;


// analiza r en busca de un tag de conditional define.
// retorn 0 si no encontró, 1 si es un IFDEF, 2 si es un IFNDEF y 3 si es  un ELSE
// en s_pre retorna el string PREVIO, en s_id retonra el CONDITIONAL para los
// casos de respuesta 1 o 2 y vacío en caso de respueta 3
function next_cond(var s_pre, s_id, s_pos: string; const r: string): integer;
var
  sup: string;
  res: string;
  iPos_min: integer;
  kPos_min: integer;

  procedure mut(kPos, iPos: integer);
  var
    i: integer;
  begin
    if iPos > 0 then
      i := iPos
    else
      i := MaxInt;

    if i < iPos_min then
    begin
      iPos_min := i;
      kPos_min := kPos;
    end;
  end;

  procedure Calc_Split(cadena: string);
  var
    iCierre: integer;
  begin
    s_pre := copy(r, 1, iPos_min - 1);
    s_pos := copy(r, iPos_min + length(cadena), MaxInt);
    iCierre := pos('}', s_pos);
    if iCierre = 0 then
      raise Exception.Create('ERROR, no se encontró el cierre "}"');
    s_id := trim(copy(s_pos, 1, iCierre - 1));
    Delete(s_pos, 1, iCierre);
  end;

begin
  sup := UpperCase(r);
  iPos_min:= pos( '//', sup );
  if iPos_min > 0 then
   delete( sup,iPos_min, length( sup ) - iPos_min + 1 );

  kPos_min := -1;
  iPos_min := MaxInt;

  mut(0, pos(s_IFDEF, sup));
  mut(1, pos(s_IFNDEF, sup));
  mut(2, pos(s_ELSE, sup));
  mut(3, pos(s_ENDIF, sup));

  case kPos_min of
    0: calc_split(s_IFDEF);
    1: calc_split(s_IFNDEF);
    2: calc_split(s_ELSE);
    3: calc_split(s_ENDIF);
  end;
  Result := kPos_min + 1;
end;



type
  TEstado = (
    CE_LIBRE,
    CE_IFDEF,
    CE_IFNDEF,
    CE_ELSE_IFDEF,
    CE_ELSE_IFNDEF);

procedure TDirectoryScanner_cleanconditionaldefine.procesar_archivo(
  const carpeta, nombre_archivo: string);
var
  f: textfile;
  r: string;
  sarchi_ent, sarchi_sal: string;
  cnt_Aperturas: integer;
  tipo_cond: integer;
  s_pre, s_id, s_pos: string;
  Estado: TEstado;
  flg_salida: boolean;
  fsal: textfile;
  cnt_incidencias: integer;

begin
  sarchi_ent := self.archi(carpeta, nombre_archivo);

  cnt_incidencias:= 0;
  write('.');
  filemode := 0;
  assignfile(f, sarchi_ent);
  reset(f);

  sarchi_sal := archi('c:\basura', nombre_archivo);
  filemode := 1;
  assignfile(fsal, sarchi_sal);
  rewrite(fsal);

  cnt_Aperturas := 0;
  Estado := CE_LIBRE;
  while not EOF(f) do
  begin
    readln(f, r);
//    writeln('-----------');
//    writeln(r);

    while r <> '' do
    begin
      tipo_cond := next_cond(s_pre, s_id, s_pos, r);
      flg_salida := Estado in [CE_LIBRE, CE_IFNDEF, CE_ELSE_IFDEF];
      case tipo_cond of
        0:  //nada
        begin
          if flg_salida then
            Write(fsal, r);
          r := '';
        end;
        1: // IFDEF
        begin
          if Estado <> CE_LIBRE then
            Inc(cnt_Aperturas);
          if flg_salida then
          begin
            if s_id = conditional_define then
            begin
              inc( cnt_incidencias );
              Estado := CE_IFDEF;
              cnt_aperturas := 0;
              Write(fsal, s_pre);
              r := s_pos;
            end
            else
            begin
              Write(fsal, s_pre + s_IFDEF + s_id + s_Cierre);
              r := s_pos;
            end;
          end
          else
            r := s_pos;
        end;
        2: // IFNDEF
        begin
          if Estado <> CE_LIBRE then
            Inc(cnt_Aperturas);
          if flg_salida then
          begin
            if s_id = conditional_define then
            begin
              inc( cnt_incidencias );
              Estado := CE_IFNDEF;
              cnt_aperturas := 0;
              Write(fsal, s_pre + ' ');
              r := s_pos;
            end
            else
            if flg_salida then
            begin
              Write(fsal, s_pre + s_IFNDEF + s_id + s_Cierre);
              r := s_pos;
            end;
          end
          else
            r := s_pos;
        end;
        3: // ELSE
        begin
          if cnt_Aperturas = 0 then
          begin
            case Estado of
              CE_IFDEF: Estado := CE_ELSE_IFDEF;
              CE_IFNDEF: Estado := CE_ELSE_IFNDEF;
            end;
            if flg_salida then
              if Estado <> CE_LIBRE then
                Write(fsal, s_pre)
              else
                Write(fsal, s_pre + s_ELSE+ s_Cierre);
          end
          else
          if flg_salida then
            Write(fsal, s_pre+ s_ELSE + s_Cierre);
          r := s_pos;
        end;
        4: // ENDIF
        begin
          if Estado <> CE_LIBRE then
          begin
            if cnt_Aperturas = 0 then
            begin  // Es el ENDIF de cierre del CONDTIONAL limpiando
              Estado := CE_LIBRE;
              if flg_salida then
                Write(fsal, s_pre);
            end
            else
            begin
              if flg_salida then
                Write(fsal, s_pre + s_ENDIF + s_Cierre);
              Dec(cnt_Aperturas);
            end;
          end
          else // Si el estado es CE_LIBRE es un cierre de otro
            Write(fsal, s_pre + s_ENDIF + s_Cierre);
          r := s_pos;
        end
      end; // case
    end;  // while
    writeln(fsal);
  end;
  closefile(fsal);
  closefile(f);
  if cnt_incidencias > 0 then
  begin
    DeleteFile( sarchi_ent );
    RenameFile( sarchi_sal, sarchi_ent);
     writeln('ATENCION:: ', sarchi_ent );
  end
  else
    deletefile( sarchi_sal );
end;


end.
