#!/usr/bin/perl

use common::sense;

use CGI;
use CGI::Carp 'fatalsToBrowser';
use RDF::Trine;
use RDF::RDFa::Generator;
use WordNet::QueryData;

my $wn  = WordNet::QueryData->new;
my $cgi = CGI->new;

my $data = {};
my $input  = rdfuri_partdecode($cgi->param('noun'));
my @senses = $wn->querySense($input.'#n');

if (!@senses)
{
   print $cgi->header(-status=>404, -type=>'text/plain');
   print "Not found." and exit;
}

sub NS ($)
{
   my $pfx = shift;
   return sub {
      my $sfx = shift;
      return $pfx.$sfx;
      };
}

my $SKOS = NS 'http://www.w3.org/2004/02/skos/core#';
my $OWL  = NS 'http://www.w3.org/2002/07/owl#';
my $FOAF = NS 'http://xmlns.com/foaf/0.1/';
my $RDF  = NS 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
my $RDFS = NS 'http://www.w3.org/2000/01/rdf-schema#';
my $WN   = NS 'http://ontologi.es/WordNet/vocab#';

my $GCLASS = rdfuri_for_generalclass($input);
my $G_count = 0;

push @{  $data->{ $GCLASS }{$RDF->('type')}  },
   {
      value => $RDFS->('Class'),
      type  => 'uri',
   };

push @{  $data->{ $GCLASS }{$OWL->('equivalentClass')}  },
   {
      value => 'http://xmlns.com/wordnet/1.6/'.rdfuri_partencode($input),
      type  => 'uri',
   };

push @{  $data->{ $GCLASS }{$RDFS->('label')}  },
   {
      value => ucwords($input),
      lang  => 'en',
      type  => 'literal',
   };

my $n = ($input =~ /^[aeiou]/i) ? 'n' : '';
push @{  $data->{ $GCLASS }{$RDFS->('comment')}  },
   {
      value => "Instances of this class are (roughly speaking) those things where it's correct to say 'this is a${n} " . ucwords($input) . "', for any reasonable interpretation of the word '" . ucwords($input) . "'.",
      lang  => 'en',
      type  => 'literal',
   };

push @{  $data->{ $GCLASS }{$OWL->('unionOf')}  },
   {
      value => '_:list' . $G_count,
      lang  => 'en',
      type  => 'blank',
   };

push @{ $data->{ $WN->('WordNet30') }{$RDF->('type')} },
   {
      value => $SKOS->('ConceptScheme'),
      type  => 'uri',
   };
   
push @{ $data->{ $WN->('WordNet30') }{$RDFS->('label')} },
   {
      value => "WordNet 3.0",
      lang  => 'en',
      type  => 'literal',
   };
   
push @{ $data->{ $WN->('WordNet30') }{$RDFS->('comment')} },
   {
      value => "WordNet 3.0 Copyright 2006 by Princeton University. All rights reserved. THIS SOFTWARE AND DATABASE IS PROVIDED \"AS IS\" AND PRINCETON UNIVERSITY MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PRINCETON UNIVERSITY MAKES NO REPRESENTATIONS OR WARRANTIES OF MERCHANT- ABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE, DATABASE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.",
      lang  => 'en',
      type  => 'literal',
   };

foreach my $sense (@senses)
{
   my @syns    = $wn->querySense($sense, "syns");
   my %labels  = map {
      (my $label  = [$_ =~ m/^([^#]+)/]->[0]) =~ s/_/ /g;
      $_ => $label; } @syns;
   my $CONCEPT = rdfuri_for_concept($syns[0]);
   my $CLASS   = rdfuri_for_class($syns[0]);

   push @{  $data->{ '_:list' . $G_count }{$RDF->('type')}  },
      {
         value => $RDF->('List'),
         type  => 'uri',
      };

   push @{  $data->{ '_:list' . $G_count }{$RDF->('first')}  },
      {
         value => $CLASS,
         type  => 'uri',
      };

   push @{  $data->{ '_:list' . $G_count }{$RDF->('rest')}  },
      {
         value => '_:list' . (++$G_count),
         type  => 'blank',
      };

   push @{  $data->{ $CONCEPT }{$RDF->('type')}  },
      {
         value => $SKOS->('Concept'),
         type  => 'uri',
      };
   
   push @{  $data->{ $CONCEPT }{$FOAF->('focus')}  },
      {
         value => $CLASS,
         type  => 'uri',
      };
   
   push @{  $data->{ $CONCEPT }{$SKOS->('prefLabel')}  },
      {
         value => $labels{ $syns[0] },
         lang  => 'en',
         type  => 'literal',
      };

   push @{  $data->{ $CONCEPT }{$SKOS->('altLabel')}  },
      map
      {
         {
            value => $labels{ $_ },
            lang  => 'en',
            type  => 'literal',
         }
      } @syns[1 .. $#syns];

   push @{  $data->{ $CONCEPT }{$SKOS->('notation')}  },
      map
      {
         {
            value => $_,
            datatype => $WN->('wn3'),
            type  => 'literal',
         }
      } @syns;
      
   push @{  $data->{ $CONCEPT }{$SKOS->('definition')}  },
      {
         value => $wn->querySense($sense, "glos"),
         lang  => 'en',
         type  => 'literal',
      };

   push @{  $data->{ $CONCEPT }{$SKOS->('broaderTransitive')}  },
      map
      {
         {
            value => rdfuri_for_concept($_),
            type  => 'uri',
         }
      }
      $wn->querySense($sense, "hype");

   push @{  $data->{ $CONCEPT }{$SKOS->('narrowerTransitive')}  },
      map
      {
         {
            value => rdfuri_for_concept($_),
            type  => 'uri',
         }
      }
      $wn->querySense($sense, "hypo");
      
   push @{  $data->{ $CONCEPT }{$WN->('meronym')}  },
      map
      {
         {
            value => rdfuri_for_concept($_),
            type  => 'uri',
         }
      }
      $wn->querySense($sense, "mero");

   push @{  $data->{ $CONCEPT }{$WN->('holonym')}  },
      map
      {
         {
            value => rdfuri_for_concept($_),
            type  => 'uri',
         }
      }
      $wn->querySense($sense, "holo");

   push @{  $data->{ $CONCEPT }{$WN->('antonym')}  },
      map
      {
         {
            value => rdfuri_for_concept($_),
            type  => 'uri',
         }
      }
      $wn->querySense($sense, "ants");

   push @{ $data->{ $CONCEPT }{$SKOS->('inScheme')} },
      {
         value => $WN->('WordNet30'),
         type  => 'uri',
      };
      
   push @{  $data->{ $CLASS }{$RDF->('type')}  },
      {
         value => $RDFS->('Class'),
         type  => 'uri',
      };
   
   push @{  $data->{ $CLASS }{$RDFS->('label')}  },
      {
         value => $labels{ $syns[0] },
         lang  => 'en',
         type  => 'literal',
      };

   push @{  $data->{ $CLASS }{$RDFS->('comment')}  },
      {
         value => $wn->querySense($sense, "glos"),
         lang  => 'en',
         type  => 'literal',
      };

   push @{  $data->{ $CLASS }{$RDFS->('subClassOf')}  },
      {
         value => $GCLASS,
         type  => 'uri',
      };

   push @{  $data->{ $CLASS }{$RDFS->('seeAlso')}  },
      {
         value => $CONCEPT,
         type  => 'uri',
      };
      
   push @{  $data->{ $CLASS }{$RDFS->('subClassOf')}  },
      map
      {
         {
            value => rdfuri_for_class($_),
            type  => 'uri',
         }
      }
      $wn->querySense($sense, "hypes");
   
}

$data->{ '_:list' . ($G_count-1) }{$RDF->('rest')}[0] =
   {
      value => $RDF->('nil'),
      type  => 'uri',
   };
   
my $model = RDF::Trine::Model->temporary_model;
$model->add_hashref($data);
my $ser   = RDF::Trine::Serializer->negotiate(namespaces=>{
   skos  => 'http://www.w3.org/2004/02/skos/core#',
   foaf  => 'http://xmlns.com/foaf/0.1/',
   rdfs  => 'http://www.w3.org/2000/01/rdf-schema#',
   wn    => 'http://ontologi.es/WordNet/concept/',
   class => 'http://ontologi.es/WordNet/class/',
   wnv   => 'http://ontologi.es/WordNet/vocab#',
   owl   => 'http://www.w3.org/2002/07/owl#',
   },
   style => 'HTML::Pretty',
   title => ucwords($input),
   );
if ($ser->can('media_types'))
{
   print $cgi->header([$ser->media_types]->[0]);
}
elsif ((ref $ser) =~ /RDFa/i)
{
   print $cgi->header(
      -type=>'text/html;charset=utf-8',
      -link=>'</WordNet/style.css>;rel="stylesheet";type="text/css"',
      );
}
else
{
   print $cgi->header('application/octet-stream');
}

print $ser->serialize_model_to_string($model) and exit;

sub ucwords ($)
{
   (my $str = lc shift) =~ s/\b(\w)/\u$1/g;
   return $str;
}

sub rdfuri_partdecode
{
   my $r = lc shift;
   $r =~ s/_/ /g;
   return $r;
}

sub rdfuri_partencode
{
   my $r = lc shift;
   $r =~ s/_/ /g;
   $r = ucwords $r;
   $r =~ s/ /_/g;
   return $r;
}

sub rdfuri_for_concept
{
   my ($word, undef, $sense) = split /#/, shift;
   return 'http://ontologi.es/WordNet/concept/' . rdfuri_partencode($word) . "-" . $sense;
}

sub rdfuri_for_class
{
   my ($word, undef, $sense) = split /#/, shift;
   return 'http://ontologi.es/WordNet/class/' . rdfuri_partencode($word) . "-" . $sense;
}

sub rdfuri_for_generalclass
{
   my ($word, undef, $sense) = split /#/, shift;
   return 'http://ontologi.es/WordNet/class/' . rdfuri_partencode($word);
}