(************************************************************************
 *                                                                      *
 *   Ant Movie Catalog 4.x                                              *
 *   (C) 2005-2011 Antoine Potten, Mickaėl Vanneufville                 *
 *   http://www.antp.be/software                                        *
 *                                                                      *
 ************************************************************************
 *                                                                      *
 *   This program is free software; you can redistribute it and/or      *
 *   modify it under the terms of the GNU General Public License        *
 *   as published by the Free Software Foundation; either version 2     *
 *   of the License, or (at your option) any later version.             *
 *                                                                      *
 *   This program is distributed in the hope that it will be useful,    *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of     *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the      *
 *   GNU General Public License for more details.                       *
 *                                                                      *
 ************************************************************************)

unit import2_engines;

interface

uses
  Windows, Classes, Types, SysUtils, ComCtrls, IniFiles,

  movieclass, interfaces, fields;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

const
  ConnectStringAccess = 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=%s;Mode=Read;Persist Security Info=False';

type

  TFieldCorresp = record
    SourceField: Integer;
    TargetField: Integer;
  end;
  TFieldCorresps = array of TFieldCorresp;

  TImportEngine = class(TObject)
  private
  protected
    FFormatName: string;
    procedure DoImport(const AFileName: TFileName; AListView: TListView); virtual;
    procedure AfterImport(const AListView: TListView); virtual;
  public
    Properties: TCustomFieldsProperties; // ref
    constructor Create(const AFormatName: string; CustomFieldsProperties: TCustomFieldsProperties);
    destructor Destroy; override;
    procedure Import(const AFileName: TFileName; AListView: TListView);
    function GetURL: string; virtual;
    property FormatName: string read FFormatName;
    function GetFilter: string; virtual;
    procedure GetFieldCorresp(out fc: TFieldCorresps; Columns: TListColumns); virtual;
    function AdjustValue(const ATargetField: Integer; AValue: string): string; virtual;
  end;

  TImportEngineCsv = class(TImportEngine)
  private
  protected
    procedure ImportFromCsv(const AStream: TStream; AListView: TListView; const Delim, Quote: Char);
    procedure DoImport(const AFileName: TFileName; AListView: TListView); override;
    procedure AfterImport(const AListView: TListView); override;
  public
    function GetFilter: string; override;
    function AdjustValue(const ATargetField: Integer; AValue: string): string; override;
  end;

  TImportEngineAmc = class(TImportEngine)
  private
    FList: TMovieList;
  protected
    procedure DoImport(const AFileName: TFileName; AListView: TListView); override;
    procedure AfterImport(const AListView: TListView); override;
  public
    destructor Destroy; override;
    function GetFilter: string; override;
    function GetURL: string; override;
    procedure GetFieldCorresp(out fc: TFieldCorresps; Columns: TListColumns); override;
  end;

  TImportEngineDll = class(TImportEngine)
  private
  protected
    FDll: Cardinal;
  public
    constructor Create(const AFormatName: string; CustomFieldsProperties: TCustomFieldsProperties);
    destructor Destroy; override;
  end;

  TImportEngineMdb = class(TImportEngineDll)
  private
  protected
    procedure ImportFromMdb(const AFileName: TFileName; AListView: TListView; AFrom, AWhere: string);
    procedure DoImport(const AFileName: TFileName; AListView: TListView); override;
    procedure AfterImport(const AListView: TListView); override;
  public
    function GetFilter: string; override;
    procedure ListTables(const AFileName: TFileName; AList: TStrings);
  end;

  TImportEngineXmm = class(TImportEngineMdb)
  private
    FDatabaseFileName: TFileName;
  protected
    procedure DoImport(const AFileName: TFileName; AListView: TListView); override;
    procedure AfterImport(const AListView: TListView); override;
  public
    function GetFilter: string; override;
    function GetURL: string; override;
    function AdjustValue(const ATargetField: Integer; AValue: string): string; override;
  end;

  TImportEngineMlb = class(TImportEngineDll)
  private
  protected
    procedure ImportFromMlb(const AFileName: TFileName; AListView: TListView);
    procedure DoImport(const AFileName: TFileName; AListView: TListView); override;
    procedure AfterImport(const AListView: TListView); override;
  public
    function GetFilter: string; override;
  end;

  TImportEngineBdv = class(TImportEngineMlb)
  private
    FDatabaseFileName: TFileName;
  protected
    procedure DoImport(const AFileName: TFileName; AListView: TListView); override;
    procedure AfterImport(const AListView: TListView); override;
  public
    function GetFilter: string; override;
    function AdjustValue(const ATargetField: Integer; AValue: string): string; override;
  end;

  TImportEngineOgs = class(TImportEngine)
  private
  protected
    procedure DoImport(const AFileName: TFileName; AListView: TListView); override;
    procedure AfterImport(const AListView: TListView); override;
  public
    function GetFilter: string; override;
    function GetURL: string; override;
  end;

  TImportEngineDvdpro = class(TImportEngine)
  private
  protected
    procedure DoImport(const AFileName: TFileName; AListView: TListView); override;
    procedure AfterImport(const AListView: TListView); override;
  public
    function GetFilter: string; override;
    function GetURL: string; override;
  end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

implementation

uses
  Math, Global,
  JvSimpleXml,
  ConstValues, functions_str;

{-------------------------------------------------------------------------------
  TImportEngine
-------------------------------------------------------------------------------}

constructor TImportEngine.Create(const AFormatName: string;
  CustomFieldsProperties: TCustomFieldsProperties);
begin
  FFormatName := AFormatName;
  Properties := CustomFieldsProperties;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

destructor TImportEngine.Destroy;
begin
  inherited;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TImportEngine.GetURL: string;
begin
  Result := '';
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TImportEngine.Import(const AFileName: TFileName; AListView: TListView);
var
  MaxCol: Integer;
  i: Integer;
begin
  if AFileName = '' then
    Abort;
  AListView.Items.BeginUpdate;
  try
    AListView.Clear;
    AListView.Columns.Clear;
    DoImport(AFileName, AListView);
    MaxCol := 1;
    for i := 0 to AListView.Items.Count-1 do
      MaxCol := Max(MaxCol, AListView.Items[i].SubItems.Count + 1);
    for i := 0 to MaxCol - 1 do
      with AListView.Columns.Add do
      begin
        Tag := -1;
        Width := 75;
      end;
    AfterImport(AListView);
    for i := 0 to AListView.Columns.Count - 1 do
      with AListView.Columns[i] do
        if Tag = fieldPicture then
          Caption := strFieldPicture
        else if Tag in (AllFields - VirtualFields) then
          Caption := strFields[Tag]
        else if (Tag >= customFieldLow) and
          (Tag - customFieldLow < Properties.Count) then
          Caption := Properties.Objects[Tag - customFieldLow].FieldName
        else
          Tag := -1;
  finally
    AListView.Items.EndUpdate;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TImportEngine.AfterImport(const AListView: TListView);
begin

end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TImportEngine.DoImport(const AFileName: TFileName; AListView: TListView);
begin

end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TImportEngine.GetFilter: string;
begin
  Result := '*.*|*.*';
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TImportEngine.GetFieldCorresp(out fc: TFieldCorresps; Columns: TListColumns);
var
  i: Integer;
begin
  SetLength(fc, Columns.Count);
  for i := 0 to Columns.Count-1 do
  begin
    fc[i].SourceField := -1;
    fc[i].TargetField := Columns[i].Tag;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TImportEngine.AdjustValue(const ATargetField: Integer; AValue: string): string;
begin
  Result := AValue;
end;

{-------------------------------------------------------------------------------
  TImportEngineCsv
-------------------------------------------------------------------------------}

procedure TImportEngineCsv.DoImport(const AFileName: TFileName; AListView: TListView);
var
  f: TFileStream;
begin
  f := TFileStream.Create(AFileName, fmOpenRead);
  try
    ImportFromCsv(f, AListView, StrToChar(Settings.rImport.rCsv.Delim), StrToChar(Settings.rImport.rCsv.Quote));
  finally
    f.Free;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TImportEngineCsv.AfterImport(const AListView: TListView);
var
  i, fld: Integer;
  s: string;
begin
  if (Settings.rImport.rCsv.FirstLineHeaders) and (AListView.Items.Count > 0) then
  begin
    if Settings.rImport.AutoAssign then
    begin
      for i := 0 to AListView.Columns.Count-1 do
      begin
        if i = 0 then
          s := AListView.Items[0].Caption
        else
        if i <= AListView.Items[0].SubItems.Count then
          s := AListView.Items[0].SubItems[i-1]
        else
          Break;
        if (s = strFieldPicture) or (s = strTagFieldPicture) then
          fld := fieldPicture
        else
        begin
          fld := strFields.IndexOf(s);
          if fld = -1 then
            fld := IndexText(s, strTagFields);
          if fld = -1 then
          begin
            fld := Properties.IndexOf(s);
            if fld <> -1 then
              fld := fld + customFieldLow;
          end
        end;
        if (fld <> -1) and not (fld in VirtualFields) then
          with AListView.Columns[i] do
            Tag := fld;
      end;
    end;
    AListView.Items.Delete(0);
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TImportEngineCsv.GetFilter: string;
begin
  Result := DialogCSVFilter;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TImportEngineCsv.ImportFromCsv(const AStream: TStream; AListView: TListView; const Delim, Quote: Char);
var
  c: Char;
  s: string;
  CurItem: TListItem;
  InStr: Boolean;
begin
  CurItem := nil;
  s := '';
  InStr := False;
  while AStream.Read(c, 1) > 0 do
  begin
    if c = Quote then
    begin
      if (s = '') and not InStr then
      begin
        InStr := True;
        Continue;
      end;
      AStream.Read(c, 1);
      if c <> Quote then
      begin
        if InStr then
        begin
          InStr := False;
          AStream.Seek(-1, soFromCurrent);
          Continue;
        end;
      end;
    end;
    if (c in [#13, #10]) and (CurItem <> nil) then
    begin
      CurItem.SubItems.Add(s);
      CurItem := nil;
      s := '';
      AStream.Read(c, 1);
      if not (c in [#13, #10]) then // support for linux/mac/win linebreaks
        AStream.Seek(-1, soFromCurrent);
      Continue;
    end;
    if ((c = Delim) and not InStr) or ((c in [#13, #10]) and (CurItem = nil)) then
    begin
      if CurItem = nil then
      begin
        CurItem := AListView.Items.Add;
        CurItem.Checked := True;
        CurItem.Caption := s;
      end
      else
        CurItem.SubItems.Add(s);
      s := '';
      if c in [#13, #10] then
      begin
        CurItem := nil;
        AStream.Read(c, 1);
        if not (c in [#13, #10]) then // support for linux/mac/win linebreaks
          AStream.Seek(-1, soFromCurrent);
      end;
      Continue;
    end;
    s := s + c;
  end;
  if (CurItem <> nil) then
    CurItem.SubItems.Add(s);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TImportEngineCsv.AdjustValue(const ATargetField: Integer; AValue: string): string;
begin
  Result := AValue;
  with Settings.rImport.rCsv do
    if (Linebreaks <> '') then
    begin
      if (ATargetField < fieldCount) and (GetFieldType(ATargetField) = ftText) then
        Result := StringReplace(AValue, Linebreaks, sLineBreak, [rfReplaceAll])
      else if (ATargetField >= customFieldLow) and (ATargetField - customFieldLow < Properties.Count) and
        (Properties.Objects[ATargetField - customFieldLow].FieldType = ftText) then
        Result := StringReplace(AValue, Linebreaks, sLineBreak, [rfReplaceAll]);
    end;
end;

{-------------------------------------------------------------------------------
  TImportEngineAmc
-------------------------------------------------------------------------------}

procedure TImportEngineAmc.DoImport(const AFileName: TFileName; AListView: TListView);
var
  i, f: Integer;
  Mov: TMovie;
  Item: TListItem;
begin
  Assert(fieldLow = 0);
  FreeAndNil(FList);
  FList := TMovieList.Create;
  if LowerCase(ExtractFileExt(AFileName)) = extCatalog[extXML] then
    FList.LoadFromXML(AFileName)
  else
    FList.LoadFromFile(AFileName);
  for i := 0 to FList.Count-1 do
  begin
    Mov := FList[i];
    Item := AListView.Items.Add;
    Item.Data := Mov;
    Item.Checked := True;
    Item.Caption := Mov.GetFieldValue(fieldLow, True);
    for f := FieldLow+1 to fieldCount-1 do
      if not (f in VirtualFields) then
        Item.SubItems.Add(Mov.GetFieldValue(f, True));
    if Mov.Picture = nil then
      Item.SubItems.Add(ExtractFileName(Mov.strPicture))
    else
      Item.SubItems.Add(Format('<%s>', [strFieldPicture]));
    with FList.CustomFieldsProperties do
      for f := 0 to Count-1 do
        Item.SubItems.Add(Mov.CustomFields.GetFieldValue(Strings[f], False, True));
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TImportEngineAmc.AfterImport(const AListView: TListView);
var
  c, f: Integer;
begin
  if Settings.rImport.AutoAssign then
  begin
    c := 0;
    for f := fieldLow to fieldCount-1 do
      if not (f in VirtualFields) and (c < AListView.Columns.Count) then
      begin
        AListView.Columns[c].Tag := f;
        Inc(c);
      end;
    if c < AListView.Columns.Count then
      AListView.Columns[c].Tag := fieldPicture;
    Inc(c);
    with FList.CustomFieldsProperties do
      for f := 0 to Count-1 do
        if c < AListView.Columns.Count then
        begin
          AListView.Columns[c].Tag := Properties.IndexOf(Strings[f]);
          if AListView.Columns[c].Tag <> -1 then
            AListView.Columns[c].Tag := AListView.Columns[c].Tag + customFieldLow;
          Inc(c);
        end;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TImportEngineAmc.GetFilter: string;
begin
  Result := DialogCatalogFilter;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TImportEngineAmc.GetURL: string;
begin
  Result := 'http://www.antp.be/software/moviecatalog/';
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

destructor TImportEngineAmc.Destroy;
begin
  FreeAndNil(FList);
  inherited;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TImportEngineAmc.GetFieldCorresp(out fc: TFieldCorresps; Columns: TListColumns);
var
  c, f: Integer;
begin
  SetLength(fc, Columns.Count);
  c := 0;
  for f := fieldLow to fieldCount-1 do
    if not (f in VirtualFields) and (c < Columns.Count) then
    begin
      fc[c].SourceField := f;
      fc[c].TargetField := Columns[c].Tag;
      Inc(c);
    end;
  if c < Columns.Count then
  begin
    fc[c].SourceField := fieldPicture;
    fc[c].TargetField := Columns[c].Tag;
    Inc(c);
  end;
  with Properties do
    for f := 0 to Properties.Count-1 do
      if (c < Columns.Count) then
      begin
        fc[c].SourceField := f + customFieldLow;
        fc[c].TargetField := Columns[c].Tag;
        Inc(c);
     end;
end;

{-------------------------------------------------------------------------------
  TImportEngineDll
-------------------------------------------------------------------------------}

constructor TImportEngineDll.Create(const AFormatName: string;
  CustomFieldsProperties: TCustomFieldsProperties);
begin
  inherited;
  FDll := LoadLibrary(PChar(strDirApp + strFileExchangeDLL));
  if FDll = 0 then
    RaiseLastOSError; 
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

destructor TImportEngineDll.Destroy;
begin
  FreeLibrary(FDll);
  inherited;
end;

{-------------------------------------------------------------------------------
 TImportEngineXmm
-------------------------------------------------------------------------------}

procedure TImportEngineXmm.AfterImport(const AListView: TListView);
var
  s: string;
  i, fld: Integer;
  FieldsFound: TStringList;
  FieldsRemaining: TMovieFields;
  PictureRemaining: Boolean;
  procedure SearchField(const AField: Integer; const ASource: string);
  begin
    if ((AField = fieldPicture) and PictureRemaining) or (AField in FieldsRemaining) then
    begin
      i := FieldsFound.IndexOf(ASource);
      if i <> -1 then
      begin
        AListView.Columns[i].Tag := AField;
        if AField = fieldPicture then
          PictureRemaining := False
        else
          Exclude(FieldsRemaining, AField);
      end;
    end;
  end;
begin
  if (Settings.rImport.AutoAssign) and (AListView.Items.Count > 0) then
  begin
    FieldsRemaining := AllFields - VirtualFields;
    PictureRemaining := True;
    FieldsFound := TStringList.Create;
    try
      FieldsFound.CaseSensitive := False;
      FieldsFound.Add(AListView.Items[0].Caption);
      FieldsFound.AddStrings(AListView.Items[0].SubItems);
      SearchField(fieldNumber, 'MovieID');
      SearchField(fieldChecked, 'Seen');
      SearchField(fieldMedia, 'CatalogNo');
      SearchField(fieldMediaType, 'Media');
      SearchField(fieldSource, 'AquiredFrom');
      SearchField(fieldDate, 'DateInsert');
      SearchField(fieldBorrower, 'Loaner');
      SearchField(fieldRating, 'Rating');
      SearchField(fieldOriginalTitle, 'OriginalTitle');
      SearchField(fieldTranslatedTitle, 'Title');
      SearchField(fieldDirector, 'Director');
      SearchField(fieldProducer, 'Producer');
      SearchField(fieldCountry, 'Country');
      SearchField(fieldCategory, 'Genre');
      SearchField(fieldYear, 'Year');
      SearchField(fieldLength, 'Length');
      SearchField(fieldActors, 'Actors');
      SearchField(fieldURL, 'WebLinkScript');
      SearchField(fieldDescription, 'Plot');
      SearchField(fieldComments, 'Comments');
      SearchField(fieldVideoFormat, 'Codec');
      SearchField(fieldVideoBitrate, 'Bitrate');
      SearchField(fieldAudioFormat, 'AudioCodec');
      SearchField(fieldAudioBitrate, 'AudioBitRate');
      SearchField(fieldResolution, 'Resolution');
      SearchField(fieldFrameRate, 'FPS');
      SearchField(fieldLanguages, 'Language');
      SearchField(fieldSubtitles, 'Subtitles');
      SearchField(fieldSize, 'Filesize');
      SearchField(fieldDisks, 'Disk');
      SearchField(fieldPicture, 'Cover');
      for i := 0 to FieldsFound.Count - 1 do
      begin
        s := FieldsFound[i];
        if s = strFieldPicture then
          fld := fieldPicture
        else
        begin
          fld := strFields.IndexOf(s);
          if fld = -1 then
            fld := IndexText(s, strTagFields);
        end;
        if (fld <> -1) and (((fld = fieldPicture) and PictureRemaining) or (fld in FieldsRemaining)) then
        begin
          AListView.Columns[i].Tag := fld;
          if fld = fieldPicture then
            PictureRemaining := False
          else
          Exclude(FieldsRemaining, fld);
        end;
      end;
    finally
      FieldsFound.Free;
    end;
    AListView.Items.Delete(0);
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TImportEngineXmm.DoImport(const AFileName: TFileName; AListView: TListView);
begin
  FDatabaseFileName := AFileName;
  ImportFromMdb(AFileName, AListView, 'MOVIES', '');
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TImportEngineXmm.GetFilter: string;
begin
  Result := DialogDivxMgrFilter;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TImportEngineXmm.GetURL: string;
begin
  Result := 'http://www.binaryworks.it/extrememoviemanager/';
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TImportEngineXmm.AdjustValue(const ATargetField: Integer; AValue: string): string;
var
  lst: TStringList;
  i: Integer;
  s: string;
  fieldType: TFieldType;
begin
  Result := AValue;
  case ATargetField of
    fieldLength:
      begin
        AValue := StringReplace(AValue, ' ', '', [rfReplaceAll]);
        AValue := CharReplace(AValue, 'h', ':');
        AValue := CharReplace(AValue, 'H', ':');
        AValue := CharReplace(AValue, '.', ':');
        if Pos(':', AValue) > 0 then
          Result := IntToStr(
            60 * StrToIntDef(DelAfterChar(AValue, ':'), 0)
              +
            StrToIntTrunc(DelBeforeChar(AValue, ':'), 0)
          );
      end;
    fieldPicture:
      if AValue <> '' then
      begin
        Result := '..\Covers\' + AValue;
        if not FileExists(Result) then
        begin
          Result := ChangeFileExt(FDatabaseFileName, '_cover\' + AValue);
          if not FileExists(Result) then
            Result := '';
        end;
      end;
    fieldActors:
      if Pos('§', AValue) > 0 then
      begin
        lst := TStringList.Create;
        try
          lst.Text := StringReplace(AValue, '§', sLineBreak, [rfReplaceAll]);
          for i := lst.Count - 1 downto 0 do
          begin
            if lst[i] = '' then
              lst.Delete(i)
            else
            begin
              s := lst[i];
              s := StringReplace(s, '|', ' (', []);
              s := TextBefore(s, '|', False);
              if s <> '' then
                lst[i] := s + ')';
            end;
          end;
          Result := lst.Text;
        finally
          lst.Free;
        end;
      end;
    else
    begin
      FieldType := ftString;
      if ATargetField < fieldCount then
        FieldType := GetFieldType(ATargetField)
      else if (ATargetField >= customFieldLow) and (ATargetField - customFieldLow < Properties.Count) then
        FieldType := Properties.Objects[ATargetField - customFieldLow].FieldType;
      case FieldType of
        ftInteger:  Result := DelAfterChar(AValue, ' ');
        ftString, ftList: Result := StringReplace(AValue, '|', ', ', [rfReplaceAll]);
        ftText: Result := InsertLineBreaks(AValue);
      end;
    end;
  end;
end;

{-------------------------------------------------------------------------------
  TImportEngineMdb
-------------------------------------------------------------------------------}

procedure TImportEngineMdb.AfterImport(const AListView: TListView);
var
  i, fld: Integer;
  s: string;
begin
  if AListView.Items.Count > 0 then
  begin
    if Settings.rImport.AutoAssign then
    begin
      for i := 0 to AListView.Columns.Count-1 do
      begin
        if i = 0 then
          s := AListView.Items[0].Caption
        else
        if i <= AListView.Items[0].SubItems.Count then
          s := AListView.Items[0].SubItems[i-1]
        else
          Break;
        if SameText(s, strTagFieldPicture) then
          fld := fieldPicture
        else
          fld := IndexText(s, strTagFields);
        if (fld <> -1) and not (i in VirtualFields) then
          AListView.Columns[i].Tag := fld;
      end;
    end;
    AListView.Items.Delete(0);
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TImportEngineMdb.DoImport(const AFileName: TFileName; AListView: TListView);
begin
  if Settings.rImport.rQuery.From <> '' then
    ImportFromMdb(AFileName, AListView, Settings.rImport.rQuery.From, Settings.rImport.rQuery.Where);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TImportEngineMdb.GetFilter: string;
begin
  Result := DialogMDBFilter;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TImportEngineMdb.ImportFromMdb(const AFileName: TFileName; AListView: TListView; AFrom, AWhere: string);
var
  QueryCreator: TWrappedQueryCreator;
  Query: IWrappedQuery;
  i: Integer;
  CurItem: TListItem;
  q: string;
  procedure AddCaption(const S: string);
  begin
    if CurItem = nil then
    begin
      CurItem := AListView.Items.Add;
      CurItem.Caption := S;
      CurItem.Checked := True;
    end
    else
      CurItem.SubItems.Add(S);
  end;
begin
  @QueryCreator := GetProcAddress(FDll, 'CreateAdoQuery');
  if @QueryCreator <> nil then
  begin
    Query := QueryCreator;
    if not Query.Connect(Format(ConnectStringAccess, [AFileName])) then
      raise Exception.Create(Query.LastError);
    try
      CurItem := nil;
      q := 'SELECT * FROM ' + AFrom;
      if AWhere <> '' then
        q := Format('%s WHERE %s', [q, AWhere]);
      Query.SetSQL(q);
      if not Query.Open then
        raise Exception.Create(Query.LastError);
      try
        if (not Query.Eof) and (Settings.rImport.AutoAssign) then
        begin
          for i := 0 to Query.FieldCount-1 do
            AddCaption(Query.FieldName(i));
          CurItem := nil;
        end;
        while not Query.Eof do
        begin
          for i := 0 to Query.FieldCount-1 do
            AddCaption(Query.Value(i));
          CurItem := nil;
          Query.Next
        end;
      finally
        Query.Close;
      end;
    finally
      Query.Disconnect;
      Query := nil;
    end;
  end
  else
    RaiseLastOSError;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TImportEngineMdb.ListTables(const AFileName: TFileName; AList: TStrings);
var
  QueryCreator: TWrappedQueryCreator;
  Query: IWrappedQuery;
begin
  AList.BeginUpdate;
  try
    AList.Clear;
    @QueryCreator := GetProcAddress(FDll, 'CreateAdoQuery');
    if @QueryCreator <> nil then
    begin
      Query := QueryCreator;
      if not Query.Connect(Format(ConnectStringAccess, [AFileName])) then
        raise Exception.Create(Query.LastError);
      try
        AList.Text := Query.ListTables;
      finally
        Query.Disconnect;
        Query := nil;
      end;
    end
    else
      RaiseLastOSError;
  finally
    AList.EndUpdate;
  end;
end;

{-------------------------------------------------------------------------------
  TImportEngineMlb
-------------------------------------------------------------------------------}

procedure TImportEngineMlb.AfterImport(const AListView: TListView);
var
  i, fld: Integer;
  s: string;
begin
  if AListView.Items.Count > 0 then
  begin
    if Settings.rImport.AutoAssign then
    begin
      for i := 0 to AListView.Columns.Count-1 do
      begin
        if i = 0 then
          s := AListView.Items[0].Caption
        else
        if i <= AListView.Items[0].SubItems.Count then
          s := AListView.Items[0].SubItems[i-1]
        else
          Break;
        if SameText(s, strTagFieldPicture) then
          fld := fieldPicture
        else
          fld := IndexText(s, strTagFields);
        if (fld <> -1) and not (i in VirtualFields) then
          AListView.Columns[i].Tag := fld;
      end;
    end;
    AListView.Items.Delete(0);
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TImportEngineMlb.DoImport(const AFileName: TFileName; AListView: TListView);
begin
  ImportFromMlb(AFileName, AListView);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TImportEngineMlb.GetFilter: string;
begin
  Result := DialogMLBFilter;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TImportEngineMlb.ImportFromMlb(const AFileName: TFileName; AListView: TListView);
var
  QueryCreator: TWrappedQueryCreator;
  Query: IWrappedQuery;
  i: Integer;
  CurItem: TListItem;
  procedure AddCaption(const S: string);
  begin
    if CurItem = nil then
    begin
      CurItem := AListView.Items.Add;
      CurItem.Caption := S;
      CurItem.Checked := True;
    end
    else
      CurItem.SubItems.Add(S);
  end;
begin
  @QueryCreator := GetProcAddress(FDll, 'CreateMlbQuery');
  if @QueryCreator <> nil then
  begin
    Query := QueryCreator;
    if not Query.Connect(AFileName) then
      raise Exception.Create(Query.LastError);
    try
      CurItem := nil;
      if not Query.Open then
        raise Exception.Create(Query.LastError);
      try
        if (not Query.Eof) and (Settings.rImport.AutoAssign) then
        begin
          for i := 0 to Query.FieldCount-1 do
            AddCaption(Query.FieldName(i));
          CurItem := nil;
        end;
        while not Query.Eof do
        begin
          for i := 0 to Query.FieldCount-1 do
            AddCaption(Query.Value(i));
          CurItem := nil;
          Query.Next
        end;
      finally
        Query.Close;
      end;
    finally
      Query.Disconnect;
      Query := nil;
    end;
  end
  else
    RaiseLastOSError;
end;

{-------------------------------------------------------------------------------
  TImportEngineBdv
-------------------------------------------------------------------------------}

function TImportEngineBdv.AdjustValue(const ATargetField: Integer; AValue: string): string;
var
  H: Integer;
begin
  Result := AValue;
  case ATargetField of
    fieldDisks:   Result := IntToStr(StrToIntTrunc(AValue, 1));
    fieldLength:
      begin
        H := Pos('H', AValue);
        if H > 0 then
          Result := IntToStr((StrToIntDef(Copy(AValue, 1, H - 1), 0) * 60) + StrToIntDef(Copy(AValue, H + 1, Length(AValue)), 0));
      end;
    fieldPicture:
      begin
        Result := ChangeFileExt(FDatabaseFileName, '\' + AValue + '.jpg');
        if not FileExists(Result) then
          Result := '';
      end;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TImportEngineBdv.AfterImport(const AListView: TListView);
begin
  if (AListView.Items.Count > 0) and Settings.rImport.AutoAssign
    and (AListView.Columns.Count = 38) then
  begin
    AListView.Columns[12].Tag := fieldOriginalTitle;
    AListView.Columns[0].Tag := fieldPicture;
    AListView.Columns[1].Tag := fieldDirector;
    AListView.Columns[2].Tag := fieldYear;
    AListView.Columns[3].Tag := fieldCountry;
    AListView.Columns[4].Tag := fieldCategory;
    AListView.Columns[5].Tag := fieldLength;
    AListView.Columns[6].Tag := fieldActors;
    AListView.Columns[7].Tag := fieldDescription;
    AListView.Columns[8].Tag := fieldProducer;
    AListView.Columns[11].Tag := fieldMediaType;
    AListView.Columns[14].Tag := fieldNumber;
    AListView.Columns[22].Tag := fieldBorrower;
    AListView.Columns[25].Tag := fieldVideoFormat;
    AListView.Columns[26].Tag := fieldAudioFormat;
    AListView.Columns[27].Tag := fieldResolution;
    AListView.Columns[29].Tag := fieldVideoBitrate;
    AListView.Columns[30].Tag := fieldAudioBitrate;
    AListView.Columns[31].Tag := fieldSize;
    AListView.Columns[32].Tag := fieldFrameRate;
    AListView.Columns[33].Tag := fieldLanguages;
    AListView.Columns[35].Tag := fieldSubtitles;
    AListView.Columns[37].Tag := fieldDisks;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TImportEngineBdv.DoImport(const AFileName: TFileName; AListView: TListView);
begin
  FDatabaseFileName := AFileName;
  inherited;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TImportEngineBdv.GetFilter: string;
begin
  Result := DialogBDVFilter;
end;

{-------------------------------------------------------------------------------
  TImportEngineOgs
-------------------------------------------------------------------------------}

procedure TImportEngineOgs.AfterImport(const AListView: TListView);
begin
  if (AListView.Items.Count > 0) and Settings.rImport.AutoAssign
    and (AListView.Columns.Count = 13) then
  begin
    AListView.Columns[0].Tag := fieldOriginalTitle;
    AListView.Columns[1].Tag := fieldDescription;
    AListView.Columns[2].Tag := fieldVideoFormat;
    AListView.Columns[3].Tag := fieldSource;
    AListView.Columns[4].Tag := fieldDisks;
    AListView.Columns[5].Tag := fieldSize;
    AListView.Columns[6].Tag := fieldComments;
    AListView.Columns[7].Tag := fieldResolution;
    AListView.Columns[8].Tag := fieldCategory;
    AListView.Columns[9].Tag := fieldLanguages;
    AListView.Columns[10].Tag := fieldYear;
    AListView.Columns[11].Tag := fieldURL;
    AListView.Columns[12].Tag := fieldDate;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TImportEngineOgs.DoImport(const AFileName: TFileName; AListView: TListView);
var
  OgsFormatSettings: TFormatSettings;
  f: TextFile;
  val: string;
  i: Integer;
  function GetNextField: string;
  var
    p: Integer;
  begin
    p := Pos(#9, val);
    if p > 0 then
    begin
      Result := Copy(val, 1, p-1);
      Delete(val, 1, p);
    end
    else
    begin
      Result := val;
      val := '';
    end;
  end;
  function ExtractDate(const ADate: string): string;
  begin
    Result := Copy(ADate, 1, Pos(' ', ADate) - 1);
  end;
begin
  GetLocaleFormatSettings(GetThreadLocale, OgsFormatSettings);
  with OgsFormatSettings do
  begin
    DateSeparator := '-';
    ShortDateFormat := 'yyyy/mm/dd';
  end;
  AssignFile(f, AFileName);
  FileMode := fmOpenRead;
  Reset(f);
  Readln(f, val);
  if Pos('Export 1.0', val) = 0 then
    raise Exception.Create('Unexpected version number. Header found: ' + val);
  Readln(f, val);
  while not Eof(f) do
  begin
    Readln(f, val);
    with AListView.Items.Add do
    begin
      Caption := GetNextField;
      SubItems.Add(StringReplace(GetNextField, ';', #13#10, [rfReplaceAll]));
      SubItems.Add(GetNextField);
      SubItems.Add(GetNextField);
      SubItems.Add(IntToStr(StrToIntDef(GetNextField, 1)));
      SubItems.Add(GetNextField);
      i := SubItems.Add('Released by: ' + GetNextField);
      SubItems.Add(GetNextField);
      SubItems.Add(GetNextField);
      SubItems.Add(GetNextField);
      SubItems.Add(GetNextField);
      SubItems.Add('http://us.imdb.com/Title?' + GetNextField);
      SubItems[i] := SubItems[i] + #13#10 + StringReplace(GetNextField, ';', #13#10, [rfReplaceAll]) + #13#10 + 'OMDb number: ' + GetNextField;
      try
        SubItems.Add(DateToStr(StrToDate(ExtractDate(GetNextField), OgsFormatSettings)));
      except
        SubItems.Add('');
      end;
    end;
  end;
  CloseFile(f);
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TImportEngineOgs.GetFilter: string;
begin
  Result := DialogOrigonsFilter;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TImportEngineOgs.GetURL: string;
begin
  Result := 'http://www.origons.com';
end;

{-------------------------------------------------------------------------------
 TImportEngineDvdpro
-------------------------------------------------------------------------------}

procedure TImportEngineDvdpro.AfterImport(const AListView: TListView);
begin
  if (AListView.Items.Count > 0) and Settings.rImport.AutoAssign
{    and (AListView.Columns.Count = 13) }then
  begin
    AListView.Columns[4].Tag := fieldOriginalTitle;
    AListView.Columns[7].Tag := fieldMedia;
    AListView.Columns[8].Tag := fieldNumber;
    AListView.Columns[10].Tag := fieldCountry;
    AListView.Columns[11].Tag := fieldYear;
    AListView.Columns[13].Tag := fieldLength;
    AListView.Columns[14].Tag := fieldMediaType;
    AListView.Columns[15].Tag := fieldCategory;
    AListView.Columns[16].Tag := fieldResolution;
    AListView.Columns[17].Tag := fieldVideoFormat;
    AListView.Columns[19].Tag := fieldLanguages;
    AListView.Columns[21].Tag := fieldAudioFormat;
    AListView.Columns[22].Tag := fieldSubtitles;
    AListView.Columns[23].Tag := fieldActors;
    AListView.Columns[24].Tag := fieldDirector;
    AListView.Columns[26].Tag := fieldProducer;
    AListView.Columns[27].Tag := fieldDate;
    AListView.Columns[28].Tag := fieldDescription;
    AListView.Columns[29].Tag := fieldPicture;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

procedure TImportEngineDvdpro.DoImport(const AFileName: TFileName; AListView: TListView);
var
  xml: TJvSimpleXml;
  root, item, subitem: TJvSimpleXmlElem;
  i, j: Integer;
  Folder: TFileName;
  Value: string;
  function GetItemValue(AItem: TJvSimpleXmlElem; const AItemName: string): string;
  var
    SubItem: TJvSimpleXmlElem;
  begin
    SubItem := AItem.Items.ItemNamed[AItemName];
    if SubItem = nil then
      Result := ''
    else
      Result := SubItem.Value;
  end;
  function GetItemList(AItem: TJvSimpleXmlElem; const AItemName: string): string; overload;
  var
    i: Integer;
    SubItem: TJvSimpleXmlElem;
  begin
    Result := '';
    SubItem := AItem.Items.ItemNamed[AItemName];
    if SubItem <> nil then
      for i := 0 to SubItem.Items.Count - 1 do
      begin
        if Result <> '' then
          Result := Result + ', ';
        Result := Result + SubItem.Items[i].Value;
      end;
  end;
  function GetItemList(AItem: TJvSimpleXmlElem; const AItemName, ALastItemName: string): string; overload;
  var
    i: Integer;
    SubItem, LastItem: TJvSimpleXmlElem;
  begin
    Result := '';
    SubItem := AItem.Items.ItemNamed[AItemName];
    if SubItem <> nil then
      for i := 0 to SubItem.Items.Count - 1 do
      begin
        LastItem := SubItem.Items[i].Items.ItemNamed[ALastItemName];
        if LastItem = nil then
          Continue;
        if Result <> '' then
          Result := Result + ', ';
        Result := Result + LastItem.Value;
      end;
  end;
  function GetActors(AItem: TJvSimpleXmlElem): string;
  var
    i: Integer;
    Value, Role: string;
    SubItem: TJvSimpleXmlElem;
  begin
    Result := '';
    SubItem := AItem.Items.ItemNamed['Actors'];
    if SubItem <> nil then
      for i := 0 to SubItem.Items.Count - 1 do
      begin
        if Result <> '' then
          Result := Result + ', ';
        if SubItem.Items[i].Items.Count = 0 then
        begin
          Value := Trim(SubItem.Items[i].Properties.Value('FirstName') + ' ' + SubItem.Items[i].Properties.Value('LastName'));
          Role := Trim(SubItem.Items[i].Properties.Value('Role'));
        end
        else
        begin
          Value := Trim(SubItem.Items[i].Items.Value('FirstName') + ' ' + SubItem.Items[i].Items.Value('LastName'));
          Role := Trim(SubItem.Items[i].Properties.Value('Role'));
        end;
        if (Role <> '') then
          Result := Format('%s%s (%s)', [Result, Value, Role])
        else
          Result := Result + Value;
      end;
  end;
  function GetCredit(AItem: TJvSimpleXmlElem; const ACreditType: string): string;
  var
    i: Integer;
    Value, CrType: string;
    SubItem: TJvSimpleXmlElem;
  begin
    Result := '';
    SubItem := AItem.Items.ItemNamed['Credits'];
    if SubItem <> nil then
      for i := 0 to SubItem.Items.Count - 1 do
      begin
        if SubItem.Items[i].Items.Count = 0 then
          CrType := Trim(SubItem.Items[i].Properties.Value('CreditType'))
        else
          CrType := Trim(SubItem.Items[i].Items.Value('CreditType'));
        if (CrType = ACreditType) then
        begin
          if Result <> '' then
            Result := Result + ', ';
          if SubItem.Items[i].Items.Count = 0 then
            Value := Trim(SubItem.Items[i].Properties.Value('FirstName') + ' ' + SubItem.Items[i].Properties.Value('LastName'))
          else
            Value := Trim(SubItem.Items[i].Items.Value('FirstName') + ' ' + SubItem.Items[i].Items.Value('LastName'));
          Result := Result + Value;
        end;
      end;
  end;
begin
  Folder := ExtractFilePath(AFileName);
  xml := TJvSimpleXml.Create(nil);
  try
    xml.LoadFromFile(AFileName);
    root := xml.Root;
    if (Root = nil) or (Root.Name <> 'Collection') then
      raise Exception.Create('Root item "Collection" not found');
    for i := 0 to root.Items.Count - 1 do
    begin
      item := root.Items[i];
      if item.Name = 'DVD' then
      with AListView.Items.Add do
        begin
          Checked := True;
          Caption := GetItemValue(item, 'ID');                                  // 0
          SubItems.Add(GetItemValue(item, 'UPC'));                              // 1
          SubItems.Add(GetItemValue(item, 'ProfileTimestamp'));                 // 2
          SubItems.Add(TextBefore(GetItemValue(item, 'ProfileTimestamp'), ' '));// 3
          SubItems.Add(GetItemValue(item, 'Title'));                            // 4
          SubItems.Add(GetItemValue(item, 'SortTitle'));                        // 5
          SubItems.Add(GetItemList(item, 'Regions'));                           // 6
          SubItems.Add(GetItemValue(item, 'CollectionType'));                   // 7
          SubItems.Add(GetItemValue(item, 'CollectionNumber'));                 // 8
          SubItems.Add(GetItemValue(item, 'Rating'));                           // 9
          SubItems.Add(GetItemValue(item, 'CountryOfOrigin'));                  // 10
          SubItems.Add(GetItemValue(item, 'ProductionYear'));                   // 11
          SubItems.Add(GetItemValue(item, 'Released'));                         // 12
          SubItems.Add(GetItemValue(item, 'RunningTime'));                      // 13
          SubItems.Add(GetItemValue(item, 'CaseType'));                         // 14
          SubItems.Add(GetItemList(item, 'Genres'));                            // 15
          SubItem := item.Items.ItemNamed['Format'];
          if SubItem <> nil then
          begin
            SubItems.Add(GetItemValue(SubItem, 'FormatAspectRatio'));           // 16
            SubItems.Add(GetItemValue(SubItem, 'FormatVideoStandard'));         // 17
          end
          else
            for j := 1 to 2 do
              SubItems.Add('');
          SubItems.Add(GetItemList(item, 'Studios'));                           // 18
          Value := GetItemList(item, 'Audio', 'AudioLanguage');                 // 19
          if Value = '' then
            Value := GetItemList(item, 'Audio', 'AudioContent');
          SubItems.Add(Value);
          SubItems.Add(GetItemList(item, 'Audio', 'AudioCompression'));         // 20
          Value := GetItemList(item, 'Audio', 'AudioChannels');                 // 21
          if Value = '' then
            Value := GetItemList(item, 'Audio', 'AudioFormat');
          SubItems.Add(Value);
          SubItems.Add(GetItemList(item, 'Subtitles'));                         // 22
          SubItems.Add(GetActors(item));                                        // 23
          SubItems.Add(GetCredit(item, 'Direction'));                           // 24
          SubItems.Add(GetCredit(item, 'Writing'));                             // 25
          SubItems.Add(GetCredit(item, 'Production'));                          // 26
          SubItem := item.Items.ItemNamed['PurchaseInfo'];
          if SubItem <> nil then
            SubItems.Add(GetItemValue(SubItem, 'PurchaseDate'))                 // 27
          else
            SubItems.Add('');
          SubItems.Add(GetItemValue(item, 'Overview'));                         // 28
          if (Caption <> '') and FileExists(Folder + Caption + 'f.jpg') then
            SubItems.Add(Caption + 'f.jpg')                                     // 29
          else
            SubItems.Add('');
        end;
    end;
  finally
    xml.Free;
  end;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TImportEngineDvdpro.GetFilter: string;
begin
  Result := DialogXmlFilter;
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

function TImportEngineDvdpro.GetURL: string;
begin
  Result := 'http://www.invelos.com/';
end;

{-------------------------------------------------------------------------------
-------------------------------------------------------------------------------}

end.

