unit csm;

interface
uses classes,dialogs,sysutils,windows,types,css;

//Wählt ein Zufallszitat aus
function getCitation(out title:string):string;
//Erzeugt HTML-Seiten in denen die angegebene Struktur enthalten ist, und die anderen
//möglichst nicht
procedure MakeSites(const structureToCreate:TStructure;const structuresNotToCreate:array of TStructure;
                       const dir:string; const n:integer);
implementation
var citationList:TStringList=nil; //Die Liste mit den Zitaten

//Überprüft ob die angegebenen Strukturen nicht in den Seiten sind
function IsGraphOK(const structuresNotToCreate:array of TStructure; const sites:TSites):boolean;forward;
//Kopiert eine Struktur 1 zu 1 in die Adjazenzmatrix
procedure WriteStructureInAdjazenzMatrix(const structure:TStructure; const startNumber:integer;
                                         const matLength:integer;mat:TAdjanzenzMatrix);forward;
//Erzeugt einen Graph in dem die angegebene Struktur enthalten ist, und die anderen
//möglichst nicht
function CreateSitesGraph(const structureToCreate:TStructure;
                          const structuresNotToCreate:array of TStructure;
                          const n:integer
                         ):TSites;forward;

//Wählt ein Zufallszitat aus
function getCitation(out title:string):string;
  //Überprüft ob in der angegebenen Zeile ein Zitat, oder ein Titel steht
  function isLineAPersonInfo(const line:integer):boolean;
  begin
    Result:=citationList[line][1]=' ';
  end;
var line:integer; //Zeilennummer
begin
  //Eine zufällige Zeile wählen
  line:=random(citationList.Count);
  if isLineAPersonInfo(line) then inc(line); //Zur nächsten gehen, wenn kein Zitat drin steht
  result:=citationList[line]; //Zitat zur Rückgabe speichern

  //Titel zum Zitat suchen
  for line:=line downto 0 do
    if isLineAPersonInfo(line) then begin //Titel gefunden
      result:=citationList[line]+result; //Titel über das Zitat schreiben

      //Rausfinden von wem das Zitat stammt.
      //Das steht in der Zeile zwischen <B> und </B>.
      title:=citationList[line];
      delete(title,1,pos('<B>',title)+2);
      delete(title,pos('</B>',title),length(title));
      title:='Zitat von '+title; //Das als Titel zurückgeben
      break;
    end;
end;

//Überprüft ob die angegebenen Strukturen nicht in den Seiten sind
function IsGraphOK(const structuresNotToCreate:array of TStructure; const sites:TSites):boolean;
var i:integer;
begin
   result:=true;
   //Alle Strukturen suchen
   for i:=0 to high(structuresNotToCreate) do begin
     //Und mit CSS feststellen, ob die Strukur gefunden wurde.
     //Ist sie vorhanden ist der Graph nicht in Ordnung, ansonsten schon
     result:=not css.existsStructure(structuresNotToCreate[i], sites);
     //Ist der Graph nicht in Ordnung, Suche abbrechen
     if not result then exit;
   end;
end;

//Kopiert eine Struktur 1 zu 1 in die Adjazenzmatrix
procedure WriteStructureInAdjazenzMatrix(const structure:TStructure; //Zu kopierende Struktur
                    const startNumber:integer; //Startindex in dem Seitengraphen
                    const matLength:integer; //Länge einer Zeile/Spalte in der Matrix
                    mat:TAdjanzenzMatrix); //Der Seitengraph als Adjazenzmatrix
var way:PSiteWay; //Die aktuelle Position innerhalb der Wege
    start,ziel:integer; //Start und Zielknoten der zu erzeugenden Kante
begin
  //Den Weg durchlaufen
  way:=structure.siteWay;
  while way.nextSite<>nil do begin
    //Startknoten im Seitengraph
    start:=way.number+startNumber;
    //Zielknoten im Seitengraph
    ziel:=way.nextSite.number+startNumber;
    //Sind beide Knoten in der Matrix, und der Zielknoten ist entweder kein Weganfang,
    //oder er soll verbunden werden, da sonst kein Knoten bekannt ist, der schon mit
    //ihm verbunden ist
    if (start<matLength)and (ziel<matLength)and (
      not way.nextSite.notLinked or not structure.linkedNodes[way.nextSite.number]) then
         mat[start*matLength+ziel]:=true; //Kante einfügen
    way:=way.nextSite;
  end;
end;

//Erzeugt einen Graph in dem die angegebene Struktur enthalten ist, und die anderen
//möglichst nicht
function CreateSitesGraph(const structureToCreate:TStructure; //zu erzeugende Struktur
                 const structuresNotToCreate:array of TStructure; //nicht zu erzeugende
                 const n:integer //Seitenanzahl
         ):TSites; //Seitengraph

var i,j,  //Schleifenvariablen
    count,//Anzahl der von einen Seite aus erreichbaren anderen
    index,//Durchlaufindex für die Seitengraphen
    startNode:integer; //Knoten von dem aus die einzelnen Teilstücke verbunden werden
begin
  //Adjazenzmatrix erzeugen
  SetLength(result.linksAdjazenzMat,n*n);
  ZeroMemory(@result.linksAdjazenzMat[0],n*n*sizeof(result.linksAdjazenzMat[0]));
  result.AdjazenzMatLen:=n;
  //Seitennamen erzeugen
  Result.fileNames:=TStringList.create;
  for i:=0 to n-1 do
    Result.fileNames.Add(inttostr(i)+'.html');

  //Strukturgrapben mehrmals durch aufrufen WriteStructureInAdjazenzMatrix
  //nebeneinander in den Seitengraphen kopieren
  i:=0;
  while i<n do begin
    WriteStructureInAdjazenzMatrix(structureToCreate,i,n,result.linksAdjazenzMat);
    i:=i+structureToCreate.siteCount;
  end;

  //Adjazenzmatrix in die Knotenlistenform bringen und den Knotengrad der hinführenden
  //Kanten setzen
  SetLength(result.inDegrees,n);
  SetLength(result.links,n,0);
  for i:=0 to n-1 do begin
    //Fesstellen, wieviele Knoten von hier aus erreichbar sind
    count:=0;
    for j:=0 to n-1 do
      if Result.linksAdjazenzMat[i*n+j] then inc(count);
    //Linkliste entsprechend setzen
    SetLength(result.links[i],count);
    //und die Kanten erzeugen
    index:=0;
    for j:=0 to n-1 do
      if Result.linksAdjazenzMat[i*n+j] then begin
        Result.links[i][index]:=j; //KAnte hinzufügen
        inc(result.indegrees[j]); //Kantengrad setzen
        inc(index);
      end;
  end;

  if n=structureToCreate.siteCount then exit; //Wenn die Seitenzahl passt, ist es fertig

  //Testen, ob eine der nicht erlaubten Strukturen vorhanden ist
  if not IsGraphOK(structuresNotToCreate,result) then begin
    startNode:=0;
    //Verbindung vom ersten Knoten zum ersten Knoten im nächsten Weg erzeugen
    //Knotenliste
    SetLength(result.links[0],length(result.links[0])+1);
    result.links[0][high(result.links[0])]:=structureToCreate.siteCount;
    //Adjazenzmatrix
    Result.linksAdjazenzMat[0*n+structureToCreate.siteCount]:=true;
  end else begin //Wenn nicht wird eine Verbindungskante gesucht, die möglichst keine einfügt
    startNode:=-1;
    inc(result.inDegrees[structureToCreate.siteCount]); //Knotengrad erhöhen
    //Alle Knoten durchlaufen, um eine passende Ausgangsseite zu finden
    for i:=0 to structureToCreate.siteCount -1 do begin
      if i=0 then begin //Der erste Knoten
        //Verbindung vom ersten Knoten zum ersten Knoten im nächsten Weg erzeugen
        //Knotenliste
        SetLength(result.links[i],length(result.links[i])+1);
        result.links[i][high(result.links[i])]:=structureToCreate.siteCount;
        //Adjazenzmatrix
        Result.linksAdjazenzMat[i*n+structureToCreate.siteCount]:=true;
      end else begin
        //Verbindung vom gerade getesteten Knoten zum ersten Knoten im nächsten
        //Weg entfernen
        SetLength(result.links[i-1],length(result.links[i-1])-1);
        Result.linksAdjazenzMat[(i-1)*n+structureToCreate.siteCount]:=false;

        //Verbindung vom aktuellen Knoten zum ersten Knoten im nächsten Weg erzeugen
        SetLength(result.links[i],length(result.links[i])+1);
        result.links[i][high(result.links[i])]:=structureToCreate.siteCount;
        Result.linksAdjazenzMat[i*n+structureToCreate.siteCount]:=true;
      end;
      //Ist der Graph in Ordnung?
      if IsGraphOK(structuresNotToCreate,result) then begin
        startNode:=i; //Diesen Knoten als Startknoten wählen
        break; //die anderen nicht mehr testen
      end;
    end;
    if startNode=-1 then //Kein Knoten gefunden,
      startNode:=structureToCreate.siteCount -1;//einfach den letzten nehmen,
                                                //weil da schon eine Kante ist
  end;

  //Den ermittelten Knoten in jedem Teilgraph mit dem jeweils nächsten verbinden
  //(die Verbindung vom ersten zum zweiten Teilgraph wurde schon im Test hergestellt)
  i:=structureToCreate.siteCount;
  while i+structureToCreate.siteCount<n do begin //Alle Teilgraphen durchlaufen
    //Kante von startNode in i-ten Teilgrapgen zum nächten Wegbeginn erzeugen
    //Adjazenzmatrix
    Result.linksAdjazenzMat[(i+startNode)*n+i+structureToCreate.siteCount]:=true;
    //Knotenliste
    SetLength(result.links[i+startNode],length(result.links[i+startNode])+1);
    result.links[i+startNode][high(result.links[i+startNode])]:=i+structureToCreate.siteCount;

    inc(result.inDegrees[i+structureToCreate.siteCount]); //Knotengrad erhöhen

    //Weite zum nächsten Teilgraph
    i:=i+structureToCreate.siteCount;
  end;

  //Manchmal kommt es vor, dass Knoten nicht mehr erreichbar sind, beispielsweise,
  //wenn ein Stück von einem Weg nicht mehr in den Graphen gepasst hat.
  //In diesem Fall werden die, noch unverbundenen Knoten auf die selbe Weise
  //wie die Teilgraphen verbunden
  j:=startNode; //Ausgangsknoten der Kante
  for i:=0 to high(result.links) do
    if (Result.inDegrees[i]=0)and(i<>0){der erste Knoten ist immer erreichbar} then begin
      //Adjazenzmatrix
      Result.linksAdjazenzMat[j*n+i]:=true;
      //Knotenliste
      SetLength(result.links[j],length(result.links[j])+1);
      result.links[j][high(result.links[j])]:=i;
      //Knotengrad erhöhen
      inc(result.inDegrees[i]);
      j:=i;
    end;
end;


//Erzeugt HTML-Seiten in denen die angegebene Struktur enthalten ist, und die anderen
//möglichst nicht
procedure MakeSites(const structureToCreate:TStructure; //zu erzeugende Struktur
                    const structuresNotToCreate:array of TStructure; //nicht zu erzeugende Strukturen
                    const dir:string; //Ausgabeverzeichnis
                    const n:integer); //Seitenanzahl
  //Ein Zitat, Text und Titel
  type TCitation=record
    text,title:string;
  end;
var sites:TSites; //Seitengraph
    citations:array of TCitation; //Zitatliste
    i,j:integer; //Schleifenvariable
    site:TStringList; //Eine HTML-Seite
begin
  //Ist n zu klein?
  if n < structureToCreate.siteCount then begin
    //mit Meldung abbrechen
    ShowMessage('Leider ist n (='+IntToStr(n)+') kleiner als m (='+IntToStr(structureToCreate.siteCount)+').');
    exit;
  end;
  //Seiten erzeugen lassen
  sites:=CreateSitesGraph(structureToCreate,structuresNotToCreate,n);
  //Testen lassen, ob die "nicht vorhanden sein" Bedingung erfüllt ist
  if not IsGraphOK(structuresNotToCreate,sites) then
    ShowMessage('Leider konnte kein Graph erzeugt werden, indem die angegebenen '+
                'Strukturen nicht enthalten sind.'#13#10'Aber es wurde einer mit'+
                'der festgelegten Struktur erzeugt');
  //Zitate erzeugen
  SetLength(citations,n);
  for i:=0 to n-1 do
    citations[i].text:=getCitation(citations[i].title);
  //Seiten erzeugen
  site:=TStringList.create;
  try
    for i:=0 to n-1 do begin
      site.Clear;
      //Seitenbeginn (Header und Titel)
      site.text:='<html>'#13#10+
                 '<head>'#13#10+
                 '<title>'+citations[i].title+'</title>'#13#10+
                 '</head>'#13#10+
                 '<body>'#13#10;
      //Gesamtüberschrift, bei der ersten Seite
      if i=0 then site.text:=site.text+'<h1>Thematisch geordnete Zitatliste</h1><br>Zitat des Monats:<br>';
      //Seiteninhalt
      site.text:=site.text+
                 citations[i].text+'<br>'#13#10+
                 '<p>Ähnliche Zitate: <br>'#13#10;
      //Links anhängen
      for j:=0 to high(sites.links[i]) do
        site.add('<a href="'+IntToStr(sites.links[i][j])+'.html">'+citations[sites.links[i][j]].title+'</a><br>');
      //Seitenende
      site.text:=site.text+
                 '</p>'#13#10+
                 '</body>'#13#10+
                 '</html>';
      //Abspeichern
      site.SaveToFile(dir+IntToStr(i)+'.html');
    end;
  finally
    site.free;
  end;
end;

initialization
  //Zitate laden
  Randomize; //Zufallsgenerator für die Zufallswahl eines Zufallszitat
  citationList:=TStringList.Create;
  citationList.LoadFromFile('zitate.html'); //Zitatliste laden
finalization
  citationList.free;
end.
