unit XcodeUtils;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, PlistFile, LazFilesUtils;

const
  iPhoneOSplatform = 'iPhoneOS.platform';

// file names should utf8 encoded


// Scanning Xcode platform for available SDKs

type
  TSDKFoundEvent = procedure (const Version: String;
    const DeviceSDKName, DeviceSDKPath, SimSDKName, SimSDKPath: String) of object;

function ScanForSDK(const PlatformDir: String; FoundProc: TSDKFoundEvent): Boolean;

// Scanning for Templates

function XibTemplateDir(const PlatformDir: AnsiString): AnsiString;

type
  TScanTemplateProc = procedure ( const TemplateName, XibFileName,
    Description, IconFileName: AnsiString) of object;

procedure ScanForXibTemplates(const TemplateDir: AnsiString; Callback: TScanTemplateProc);

implementation

type
  TSDKDescription = record
    FullPath  : String;  {full SDK path}
    Name      : String;
    Alternate : String; {alternate SDK -> iphonesimulator for iphoneos}
    Version   : String;
    isSim     : Boolean; {true for real iPhoneOS, false for iPhoneSimulator}
  end;

function ReadSDKSettings(const FileName: String; var Descr: TSDKDescription): Boolean;
var
  plist : TPListFile;
begin
  Result:=False;
  plist:=TPListFile.Create;
  try
    plistfile.LoadFromFile(FileName, plist);

    Descr.Name:=GetStr(plist, 'CanonicalName');
    Descr.Alternate:=GetStr(plist, 'AlternateSDK');
    Descr.Version:=GetStr(plist, 'Version');
  finally
    plist.Free;
  end;
end;

function isSDKDir(const SDKDir: String; var d: TSDKDescription): Boolean;
var
  plist : String;
begin
  plist := IncludeTrailingPathDelimiter(SDKDir)+'SDKSettings.plist';
  Result:=FileExists(plist);
  if not Result then Exit;
  ReadSDKSettings(plist, d);
  d.FullPath:=SDKDir;
end;

function ScanForSDK(const PlatformDir: String; FoundProc: TSDKFoundEvent): Boolean;
const
  PlatformName: array [Boolean] of String = ('iPhoneOS.platform','iPhoneSimulator.platform');
  SDKSubDir = PathDelim+'Developer'+PathDelim+'SDKs'+PathDelim;
var
  isSim   : Boolean;
  dir     : String;
  sr      : TSearchRec;
  sdks    : array of TSDKDescription;
  descr   : TSDKDescription;
  cnt     : Integer;
  simname : String;
  simpath : String;
  i,j     : Integer;

  procedure AddDescription(const d: TSDKDescription);
  begin
    if cnt = length(sdks) then begin
      if cnt = 0 then SetLength(sdks, 16)
      else SetLength(sdks, cnt*2);
    end;
    sdks[cnt]:=d;
    inc(cnt);
  end;

begin
  Result:=Assigned(FoundProc);
  if not Result then Exit;

  cnt:=0;

  for isSim:=false to true do begin
    dir := IncludeTrailingPathDelimiter(PlatformDir) + PlatformName[isSim] + SDKSubDir;
    if FindFirst(dir+'*', faAnyFile, sr)=0 then begin
      repeat
        if (sr.Attr and faDirectory>0) and (ExtractFileExt(sr.Name) = '.sdk') then
          if isSDKDir( dir + sr.Name, descr) then begin
            descr.isSim:=isSim;
            AddDescription(descr);
          end;
      until FindNext(sr)<>0;
      FindClose(sr);
    end;
  end;

  for i:=0 to cnt-1 do
    if not sdks[i].isSim then begin
      simname:='';
      simpath:='';
      for j:=0 to cnt-1 do
        if (sdks[j].isSim) and (sdks[i].Alternate=sdks[j].Name) then begin
          simname:=sdks[j].Name;
          simpath:=sdks[j].FullPath;
        end;
      FoundProc(sdks[i].Version, sdks[i].Name, sdks[i].FullPath, simname, simpath);
    end;

  Result:=True;
end;


function XibTemplateDir(const PlatformDir: AnsiString): AnsiString;
const
  TemplatePath = 'Developer/Library/Xcode/File Templates/User Interface';
begin
  Result:=IncludeTrailingPathDelimiter(PlatformDir)+TemplatePath;
end;

procedure ScanForXibTemplates(const TemplateDir: AnsiString; Callback: TScanTemplateProc);
var
  dirs   : TStringList;
  files  : TStringList;
  i,j    : Integer;
  plist  : TPListFile;

  xib     : AnsiString;
  name    : AnsiString;
  descr   : AnsiString;
const
  XibTemplateMask = '*.pbfiletemplate';
  IconFile = 'TemplateIcon.tiff';
begin
  if not Assigned(Callback) or not DirectoryExists(TemplateDir) then Exit;

  dirs:=TStringList.Create;
  files:=TStringList.Create;
  try
    EnumFilesAtDir( TemplateDir, XibTemplateMask, dirs );
    for i:=0 to dirs.Count-1 do begin
      if DirectoryExists(dirs[i]) then begin
        files.Clear;
        EnumFilesAtDir(dirs[i], files);

        xib:='';
        for j:=0 to files.Count-1 do
          if AnsiLowerCase(ExtractFileExt(files[j]))='.plist' then begin
            plist:=TPListFile.Create;
            plistfile.LoadFromFile(files[j],plist);
            xib:=GetStr(plist, 'MainTemplateFile');
            descr:=GetStr(plist, 'Description');
            name:=ChangeFileExt(xib, '');
            Break;
          end;

        if xib<>'' then begin
          xib:=IncludeTrailingPathDelimiter(dirs[i])+xib;
          Callback(name, xib, descr, IncludeTrailingPathDelimiter(dirs[i])+IconFile);
        end;
      end;
    end;
  finally
    dirs.Free;
    files.Free;
  end;
end;

end.