package univis;
##############################################################################
# $Id: univis.pm,v 1.10 2006/05/19 09:17:09 unrzc9 Exp $
##############################################################################
# UnivIS-Modul
# Lesen von UnivIS-Daten ueber die PRG-Schnittstelle
# Copyright (C) 2004-2005  Wolfgang Wiese, http://www.xwolf.de
#
# univis.pm ist freie Software; Sie drfen sie unter den Bedingungen der 
# GNU Lesser General Public License, wie von der Free Software Foundation 
# ver�fentlicht, weiterverteilen und/oder modifizieren; entweder gem� 
# Version 2.1 der Lizenz oder (nach Ihrer Option) jeder sp�eren Version.
#
# univis.pm wird in der Hoffnung weiterverbreitet, da�sie ntzlich 
# sein wird, jedoch OHNE IRGENDEINE GARANTIE, auch ohne die implizierte Garantie 
# der MARKTREIFE oder der VERWENDBARKEIT F� EINEN BESTIMMTEN ZWECK. Mehr Details 
# finden Sie in der GNU Lesser General Public License.
#
# Sie sollten eine Kopie der GNU Lesser General Public License zusammen mit 
# dieser Bibliothek/diesem Programm erhalten haben; falls nicht, schreiben Sie 
# an die Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, 
# MA 02111-1307, USA.
#
##############################################################################
# Last Modified on:	$Date: 2006/05/19 09:17:09 $
# By:			$Author: unrzc9 $
# Version:		$Revision: 1.10 $ 
##############################################################################
use strict;
use LWP::UserAgent;

require Exporter;
  
our @ISA = qw(Exporter);
our @EXPORT_OK = qw( );
our @EXPORT = qw( new reset DESTROY execute univisxml2hash checkquery GetAllXJobs GetKursebyOrgnum 
                                GetPersonsbyOrgnum GetOrgDatabyPath AnalyseXMLArray Termin2String);
our $VERSION =  do { my @r = (q$Revision: 1.10 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r };
##############################################################################
# Default values
##############################################################################
$univis::defaulturl     = "http://univis.uni-erlangen.de/prg";
    # Default-URL zu UnivIS
$univis::defaultformat  = "xml";
    # Default-Ausgabeformat
%univis::module         = ("lectures" => 'department,chapter,name,shortname,type,number,sws,bonus,malus,ectscredits,lecturer,room,path,token,id,sem',
                          "persons" => 'department,name,firstname,fullname,lehrtype,xjob,title',
                          "chapters" => 'name,fullname,path',
                          "departments" => 'name,number,fullname,path',
                          "calendar" => 'department,name,fullname,subtitle,type,contributor,start,end,who',
                          "events" => 'department,title,start,end,who,contact,incalendar,linked,subtitle,type,contributor',
                          "rooms" => 'department,name,longname,contact,size,path,fullname',
                          "thesis" => 'department,title,shorttitle,type,finished,advisor,sem',
                          "publications" => 'department,year,number,name,fullname,author,editor,month,location,publisher,keywords',
                          "projects" => 'department,name,fullname,director,contact',
                          "functions" => 'department,name,fullname,person',
                          "icontacts" => 'type,name,contact,partner,country'
                         
    );
    # Definition der moeglichen Abfrageparameter

%univis::db2dbname = (
        'lectures'    => 'Lecture',
        'persons'     => 'Person',
        'rooms'       => 'Room',
        'chapters'    => 'Title',
        'functions'   => 'Org',
        'departments' => 'Org',
        'calendar'    => 'Calendar',
        'events'      => 'Event',
        'thesis'      => 'Thesis',
        'publications'=> 'Pub',
        'projects'    => 'Research',
        'icontacts'   => 'Inter',
);    
    
@univis::xjobs = ("Sicherheitsbeauftragter","IT-Sicherheits-Beauftragter","Webmaster","Postmaster","IT-Betreuer","UnivIS-Beauftragter");
# Liste der gueltigen Jobs am 10.3.2004


@univis::Organisationskeypfade = ("rrze", "med", "wiso", "erz", "sonste", "tech", "phil1", "phil2", 
"theo","zwiss","juris","zentra","natur1","natur2","natur3","univer","forsch","akadem");
           
%univis::publikationstypen = ("artmono" => "Artikel im Sammelband",
                            "arttagu" =>  "Artikel im Tagungsband",
                            "artzeit" => "Artikel in Zeitschrift",
                            "techrep" => "Interner Bericht (Technischer Bericht, Forschungsbericht)",
                            "hschri" => "Hochschulschrift (Dissertation, Habilitationsschrift, Diplomarbeit etc.)",
                            "dissvg" => "Hochschulschrift (auch im Verlag erschienen)",
                            "monogr" => "Monographie",
                            "tagband" => "Tagungsband (nicht im Verlag erschienen)",
                            "schutzr" => "Schutzrecht"  );
%univis::examensarbeitstypen = ("diplom" => "Diplomarbeit",
                            "masthes" => "Master Thesis",
                            "magister" => "Magisterarbeit",
                            "studien"=> "Studienarbeit",
                            "haus" => "Hausarbeit");

%univis::lecturetypen = (
"awa" => "Anleitung zu wiss. Arbeiten  (AWA)",
"ag" => "Arbeitsgemeinschaft  (AG)",
"ak" => "Aufbaukurs  (AK)",
"ek" => "Einf&uuml;hrungskurs  (EK)",
"ex" => "Exkursion  (EX)",
"gk" => "Grundkurs  (GK)",
"hs" => "Hauptseminar  (HS)",
"kk" => "Klausurenkurs  (KK)",
"klv" => "Klinische Visite  (KLV)",
"ko" => "Kolloquium  (KO)",
"ku" => "Kurs  (KU)",
"ms" => "Mittelseminar  (MS)",
"os" => "Oberseminar  (OS)",
"pr" => "Praktikum  (PR)",
"prs" => "Praxisseminar  (PRS)",
"pjs" => "Projektseminar  (PJS)",
"ps" => "Proseminar  (PS)",
"re" => "Repetitorium  (RE)",
"sem" => "Seminar  (SEM)",
"sl" => "Sonstige Lehrveranstaltung  (SL)",
"ts" => "Theorieseminar  (TS)",
"tut" => "Tutorium  (TUT)",
"ue" => "&Uuml;bung  (UE)",
"vorl" => "Vorlesung  (VORL)",
"v-ue" => "Vorlesung mit &Uuml;bung  (V/UE)",
"hvl" => "Hauptvorlesung  (HVL)",
"pf" => "Pr&uuml;fung  (PF)",
"gsz" => "Gremiensitzung  (GSZ)");

# Bekannte Verknuepfungen
#   xjob  in  "persons"   kann gefuellt werden mit den Inhalt aus "Description" bei der Job-Angabe in "departments"
#         wenn gleichzeitig flags = ! gesetzt ist

@univis::days = ("Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag");
@univis::wdays = ("So","Mo","Di","Mi","Do","Fr","Sa");
@univis::months = ("Januar", "Februar", "M&auml;rz", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember");
@univis::smonths = ("Jan", "Feb", "M&auml;", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez");

# extra 31 for month 0 (= Dec last year)
@univis::monthlen = (31,31,28,31,30,31,30,31,31,30,31,30,31);
@univis::monthsum = (0,0,31,59,90,120,151,181,212,243,273,304,334);
@univis::monthsum_leap = (0,0,31,60,91,121,152,182,213,244,274,305,335);



##############################################################################
sub  Termin2String {
  my $termin = shift;
  my $typ;
  my $zeitspanne;
  
    if (not $termin) {
      return;
    }
    $termin->{'startdate'} = Germandate($termin->{'startdate'});
    $termin->{'enddate'} = Germandate($termin->{'enddate'});
    if ($termin->{'repeat'} =~ /^b/i) {
      # Blockveranstaltung
      $typ = "Blockveranstaltung";
      $zeitspanne = "$termin->{'startdate'} - $termin->{'enddate'}, $termin->{'starttime'} - $termin->{'endtime'} Uhr";
    } elsif ($termin->{'repeat'} =~ /^s1/i) {
      # Einzeltermin
      $typ = "Einzeltermin";
      $zeitspanne = "$termin->{'startdate'}, $termin->{'starttime'} - $termin->{'endtime'} Uhr";
    } elsif ($termin->{'repeat'} =~ /^w(\d+)\s+(\d+)/i) {
      # Wochentags
      
      $typ = day2wday($2);
      if ($1 == 1) {
        $typ = "Jede Woche, $typ.";
      } elsif ($1 == 2) {
        $typ = "Jede zweite Woche, $typ.";
      } else {
        $typ = "Jede $1. Woche, $typ.";
      }
      $zeitspanne = "$termin->{'starttime'}  - $termin->{'endtime'} Uhr";
    } elsif (not $termin->{'repeat'}) {
      $typ = "Einzelveranstaltung";
      if (not $termin->{'startdate'}) {
        $termin->{'startdate'} = "Datum derzeit nicht angegeben"
      }
      if (($termin->{'enddate'}) && ($termin->{'startdate'} ne $termin->{'enddate'})) {
        $zeitspanne = "$termin->{'startdate'} - $termin->{'enddate'}, $termin->{'starttime'} - $termin->{'endtime'} Uhr";
      } else {        
        $zeitspanne = "$termin->{'startdate'}, $termin->{'starttime'} - $termin->{'endtime'} Uhr";      
      }
      
    }
   return "$typ, $zeitspanne";
}
##############################################################################
sub Germandate {
  my $string = shift;
  if (not $string) {
    return;
  }
  my ($jahr, $monat, $tag) = split(/\-/,$string,3);
  return "$tag.$monat.$jahr";
}
##############################################################################
sub AnalyseXMLArray {
  my $this = shift;

  if (not $this) {
    return;
  }
  my %result;  
  my $key;
  my $i;
  my $ref = ref($this);
  
  if ($ref !~ /array/i) {
     return;
  } 
  my @liste = @{$this};

  my $eintraege = scalar(@liste);
  if ($eintraege < 1) {
    return;
  }
  my $count;
  my $params;
  my $fertig;
  my $pointer;
  my $arraycount;
  my $childhash;
  my %hash;
  my $keyname;
  my $oldkey;

  if ($eintraege==3) {
    if ($liste[0]) {
        if (ref($liste[0]) =~ /hash/i) {
          if (keys %{$liste[0]}) {
            $result{'params'} = $liste[0];
          }
        } else {
          $result{'params'} = $liste[0];
        }
    }
    if ($liste[2]) {
      if ((ref($liste[2]) =~ /array/i) && (scalar(@{$liste[2]})==1)) {
        # print "Ein elementige Unterliste!\n$liste[1], $liste[2]\n";
        $result{'tagparams'} = $liste[2]->[0];
        $result{'tagparams'}{'title'} = $liste[1];
      } else {
        $result{'value'} = $liste[2];
      }
    }
    if ($liste[1]) {
        $result{'singletag'} = $liste[1];
    }
    return %result;
  } elsif ($eintraege>3) {
    if ($liste[0]) {
      if (ref($liste[0]) =~ /hash/i) {
          if (keys %{$liste[0]}) {
            $result{'params'} = $liste[0];
          }
        } else {
          $result{'params'} = $liste[0];
        }
    }
    $count = 3;
    $arraycount =0;
    while (not $fertig) {
        $arraycount++;
        %hash = AnalyseXMLArray($liste[$count+1]);
        if (%hash) {         
            %{$result{'data'}{$arraycount}} = %hash;   
        }
        if ($liste[$count]) {
          $result{'data'}{$arraycount}{'title'} = $liste[$count];
        }
        if ($liste[$count+2]) {
          $result{'data'}{$arraycount}{'tag'} = $liste[$count+2];
        }         
        $count = $count + 4;
        if ($count > $eintraege) {
           $fertig =1;
        }
      
    }
  } elsif ($eintraege==2) {
     # Starthash
     $result{'title'} = $liste[0];
     my %this = AnalyseXMLArray($liste[1]);
     
     %{$result{'data'}} = %this;
  } elsif ($eintraege==1) {
    # Eingebetteter Hash in einem 1elementigen Array: Dies kommt
    # dann vor, wenn der Tag kein Erstreckungsbereich hat sondern nur
    # Attribute.
    if (ref($liste[0]) =~ /hash/i) {
      return %{$liste[0]};
    }  
  } else {
    return;
  }
  if ($result{'data'}) {
    %{$result{'data'}} = KorrekturHashkeys($result{'data'});
  }
  return %result;
}
##############################################################################
sub KorrekturHashkeys {
  my $hashref = shift;
  if (not $hashref) {
     return;
  }
  my %hash = %{$hashref};
  my $key;
  my $num = scalar(keys %hash);
  my %titles;
  my $contentcount;
  
  foreach $key (keys %hash) {
    if ($hash{$key}{'title'} =~ /[a-z0-9]+/i) {
      $titles{$hash{$key}{'title'}} = 1;      
    }
    if (scalar(keys %{$hash{$key}}) > $contentcount) {
      $contentcount = scalar(keys %{$hash{$key}});
    }
  }
  my $numtitles = scalar(keys %titles);
  if ($numtitles==$num) {
    my %result;
    my $titel;
    my $subkey;
    foreach $key (keys %hash) {
      $titel = $hash{$key}{'title'};
      if (($contentcount==2) && ($hash{$key}{'value'})) {
        $result{$titel} = $hash{$key}{'value'};
      } else {
        foreach $subkey (keys %{$hash{$key}}) {
          next if ($subkey eq 'title');
          $result{$titel} = $hash{$key}{$subkey};
        }
      }
    }
    return %result;
  } else {
      # Alles bleibt wie es ist, da ein Titel mehrfach vorkommen kann oder
      # aber ein Inhaltstitel da ist, welcher nicht als Referenz genutzt werden kann
    return %hash;
  }
}
##############################################################################
sub GetOrgDatabyPath {
  my $self = shift;
  my $path = shift;
  my $overwrite = shift;
  
  if (not $self) {
    return;
  }
  if (not $path) {
    return;
  }
  if (($self->{'hash'}) && (not $overwrite)) {
    # Wir nehmen den vorhandenen Hasheintrag, aendern also das Objekt nicht
    # und machen also auch keine neue Anfrage.
    # In diesem Fall wirkt der Funktionsaufruf nur wie ein Filter auf die
    # bereits vorhandenen Inhalte
    
  } else {
    $self->{'query'} = "search=departments&path=$path";
    my $res = $self->execute;  
    if (not $res) {
      return;
    }
    $self->univisxml2hash;
  }
  my $key;
  my %result;
  foreach $key (keys %{$self->{'hash'}{'data'}}) {    
    if ($self->{'hash'}{'data'}{$key}{'title'} =~ /^org$/i) {
      %{$result{$key}} = %{$self->{'hash'}{'data'}{$key}};    
    }
  }
  return \%result; 
}
##############################################################################
sub GetOrgbyNum {
  my $self = shift;
  my $num = shift;
  my $overwrite = shift;
  my $key;
  my %result;
  
  if (not $self) {
    return;
  }
  if (not $num) {
    return;
  }
  if (($self->{'hash'}) && (not $overwrite)) {
    foreach $key (keys %{$self->{'hash'}{'data'}}) {    
      if (($self->{'hash'}{'data'}{$key}{'title'} =~ /^org$/i) && ($self->{'hash'}{'data'}{$key}{'data'}{'orgnr'}==$num)) {
        %{$result{$key}} = %{$self->{'hash'}{'data'}{$key}};    
      }
    }
    
  } else {
    $self->{'query'} = "search=departments&number=$num";
    my $res = $self->execute;
    if (not $res) {
      return;
    }
    $self->univisxml2hash;
    foreach $key (keys %{$self->{'hash'}{'data'}}) {    
      if ($self->{'hash'}{'data'}{$key}{'title'} =~ /^org$/i) {
        %{$result{$key}} = %{$self->{'hash'}{'data'}{$key}};    
      }
    }
  }
    
  return \%result;
}
##############################################################################
sub GetKursebyOrgnum {
  my $self = shift;
  my $orgnummer = shift;  
  my $sem = shift;
  
  if (not $self) {
    return;
  }
  if ((not $orgnummer) || ($orgnummer !~ /^\d+$/i)) {
    return;
  }
  $self->{'query'} = "search=lectures&department=$orgnummer";
  my $res = $self->execute;
    if (not $res) {
      return;
    }
  return $self->univisxml2hash;
      
}
##############################################################################
sub GetPersonsbyOrgnum {
  my $self = shift;
  my $orgnummer = shift;  
  my $sem = shift;
  
  if (not $self) {
    return;
  }
  if ((not $orgnummer) || ($orgnummer !~ /^\d+$/i)) {
    return;
  }
  $self->{'query'} = "search=persons&department=$orgnummer";
  my $res = $self->execute;
    if (not $res) {
      return;
    }
  return $self->univisxml2hash;
      
}
##############################################################################
sub GetAllXJobs {
  my $self = shift;
  my $job = shift;
  my $i;
  my $search;
  if (not $job) {
    return;
  }
  for ($i=0; $i<=$#univis::xjobs; $i++) {
    if ($univis::xjobs[$i] =~ /$job/i) {
         $search = $univis::xjobs[$i];
         last;
    }
  }
  if ($search) {
    $self->{'query'} = "search=persons&xjob=$search";
    my $res = $self->execute;
    if (not $res) {
      return;
    }
    return $self->univisxml2hash;
  } else {
     return;
  }
}          
##############################################################################
sub checkquery {
  my $query = shift;
  
  if (not $query) {
    return;
  }
  my @list = split(/&/,$query);
  my $i;
  if (not @list) {
     return;
  }
  my ($key, $value);
  my %ql;
  for ($i=0; $i<=$#list;$i++) {
    ($key,$value) = split(/=/,$list[$i],2);
    if ($key) {
      $key =~ s/[^a-z]//gi;
      $key = lc($key);
      $ql{$key} = $value;
    }
  }
  my @questions;
  if ($ql{'search'}) {
     @questions = split(/\s*,\s*/,$univis::module{$ql{'search'}});
     my %vq;
     my @valid;
     
     if (@questions) {
       push(@valid,"search=$ql{'search'}");
       for ($i=0;$i<=$#questions;$i++) {
          if ($ql{$questions[$i]}) {
            push(@valid,"$questions[$i]=$ql{$questions[$i]}");
          }
       }
       my $res;
       for ($i=0;$i<=$#valid; $i++) {
         if ($res) {
            $res .= "&".$valid[$i];
         } else {
            $res = $valid[$i];
         }
       }
       return $res;
     } else {
        return;
     }
  } else {
     return;
  }
}
##############################################################################
sub univisxml2hash {
  my $self = shift;
  if (not $self) {
    return 0;
  }
  if (not $self->{'content'}) {
    if (($self->{'url'}) && ($self->{'query'})) {
      my $res = $self->execute;
      if ($res ==1) {
        return $self->univisxml2hash;
      } else {
        return 0;
      }
    }
    return 0;
  }
  if ($self->{'content'} =~ /\s*<html>/i) {
    return;
  }
  use XML::Parser;
  my $parser = new XML::Parser( Style => 'Tree' );

  my $tree = $parser->parse($self->{'content'});  
  $self->{'xmlhash'} = $tree;
  my %hash = AnalyseXMLArray($tree);
  if ((scalar(keys %hash)==2) && ($hash{'title'}) && ($hash{'data'})) {
     my %tmp = %{$hash{'data'}};
     %hash = %tmp;
  }
  %{$self->{'hash'}} = %hash;
  
  
  return $self->{'hash'};
}
##############################################################################
sub execute {
  my $self = shift;
  my $agent = shift || "XWolf UnivIS Parser $VERSION";
  my $type = shift || "application/x-www-form-urlencoded";

  if (not $self) {
    return -1;
  }
  if (not ($self->{'query'})) {
    return -2;
  }
  our $ua = LWP::UserAgent->new;
  $ua->agent($agent);

  # Create a request
  # print "Asking URL : $self->{'url'}\n";
  our  $req = HTTP::Request->new(POST => "$self->{'url'}");
  $req->content_type($type);
  our $query = $self->{'query'};
  if ($query !~ /show=/i) {  
    $query .= "&show=".$self->{'format'};
  }
  # print "Query: $query\n";
  $req->content($query);

  # Pass request to the user agent and get a response back
  our $res = $ua->request($req);

  # Check the outcome of the response
  if ($res->is_success) {
    $self->{'content'} = $res->content;    
    return 1;
  } else {
    $self->{'errorstatus'} = $res->status_line;
    return 0;
  }
}
##############################################################################
sub new {
  my $self = {};
  my $this = shift;
  my $url = shift || $univis::defaulturl;
  my $query = shift;
  my $show = shift || $univis::defaultformat;
  

  
  bless $self;
  $self->{'url'} = $url;
  $self->{'format'} = $show;
  $self->{'query'} = $query;
  return $self;
}
##############################################################################
sub reset {
      my $self = shift;
      $self = {};
}
##############################################################################
sub DESTROY {
  my $self = shift;
  $self->reset;
  #  warn "DESTROYING $self\n";
}
##############################################################################
# Hilfsfunktionen
##############################################################################
sub day2wday {
  return $univis::wdays[shift];
}

sub str2wday {
  my $str = shift;
  return if $str eq '';
  my $i;
  for ($i=0; $i < 1+@univis::wdays; $i++) {
    last if $univis::wdays[$i] eq $str;
  }
  return $i;
}

sub eur2day {
  my $d = shift;
  return ($d==6) ? 0 : $d+1;
}

sub day2eur {
  my $d = shift;
  return ($d==0) ? 6 : $d-1;
}

sub monthlen {
  my $year = shift;
  my $month = shift;

  if ($month == 2 && leapyear($year)) {
    return 29;
  } else {
    return $univis::monthlen[$month];
  }
}

sub leapyear {
  my $year = shift;
  return ($year%400 == 0) || (($year%4 == 0) && ($year%100 != 0));
}
sub iso2date {
  my $date = shift;
  my $short = shift;

  my ($y, $m, $d) = ($date =~ /^(\d\d\d\d)-(\d\d)-(\d\d)$/);
  return unless $y;
  return $short ? sprintf("%d.%d.", $d, $m) : sprintf("%d.%d.%d", $d, $m, $y);
}
##############################################################################
1;
##############################################################################
