#!/usr/bin/perl 

#########################################
##### Author:		Sebastian Enger / B.Sc
##### CopyRight:	Sebastian Enger / B.Sc
##### LastModified	7.01.2007
##### Function:		Dublicated Content Filter für Google
##### Todo:			
########################################

# todo:
# statische synonym textdatei nehmen und diese dann abarbeiten, wenn kein treffer gefunden wurde dann benutze das langsame my $result	= Lingua::DE::Wortschatz::use_service('T', $_);
# StringManipulation ist mir noch zu krass, der text wird extrem unleserlich bis jetzt noch
###	amazon content grabbing + adding

# my $sentences			= get_sentences($text);				# returns an array ref
# my $FullContentArray	= &Main( $sentences	);
# $WordCount			= $#FullContent;

# synonyme in format bringen
# synonyme vergleichen mit textdate und zu modifizierendem text
# wenn syn. nicht in text datei vorhanden dann benutze webservice
# ergebnisse vom webservice dann auch in unsere syn. datei hinzufügen


# perl -MCPAN -e 'install "Text::German"'
# perl -MCPAN -e 'install "Text::English"'
# perl -MCPAN -e 'install "Lingua::EN:Inflect"'
# perl -MCPAN -e 'install "Lingua::DE::Wortschatz"'
# perl -MCPAN -e 'install "Lingua::DE::Num2Word"'
# perl -MCPAN -e 'install "Lingua::DE::ASCII"'
# perl -MCPAN -e 'install "Lingua::DE::Sentence"'
# perl -MCPAN -e 'install "Lingua::DE::TypoGenerator"'
# perl -MCPAN -e 'install "String::Approx"'
# perl -MCPAN -e 'install "Text::Levenshtein"'


# reniceing to 20 !
system("renice 20 $$");
system("clear");

use strict;
use Data::Dumper;
use Text::German;
use Text::English;
use Lingua::DE::ASCII;
use Lingua::EN::Inflect;
use Lingua::DE::Sentence;
use Lingua::DE::Num2Word;
use Lingua::DE::Wortschatz;
use Lingua::DE::TypoGenerator;		# generate typing errors from given keyword

# random initilisation
srand();


# variables
my $lang						= "de";
my $text						= "";
my $TextWordCounter				= 1;
my $ModifiedWordcounter			= 1;
my $FullContentArray			= "";

my @letters						= (	"a", "b", "c", "d", "e", "f", "g", "h",
									"i", "j", "k", "l", "m", "n", "o", "p",
									"q", "r", "s", "t", "u", "v", "w", "x",
									"y", "z" );

my @numbers						= ( "0", "1", "2", "3", "4", "5", "6", "7",
									"8", "9" );

my @characters					= (	"+", "-", "&", " ", "(", ")", "@", "!",
									"%" );


# lege fest, welche manipulation genommen werden soll
my $UseString					= 1;
my $UseTextBlowup				= 1;
my $UseSynonym					= 1;
my $UseZahl2Word				= 1;
my $UseGrundform				= 1;
my $UseTypingError				= 1;

# zeige später in den statistiken an, was genommen wurde
my $UsedString					= 0;
my $UsedTextBlowup				= 0;
my $UsedSynonym					= 0;
my $UsedZahl2Word				= 0;
my $UsedGrundform				= 0;
my $UsedTypingError				= 0;

my $StopWordList				= {};
my %StopWordList				= ();
my %BigLetterList				= ();

my @SynonymContent				= ();
my @FullContent					= ();
my @FullContentArray			= ();

my $MainKeyWord					= "Harry Potter";
my $SrcFile						= "src-$lang.txt";	# quell	
my $ModFile						= "mod.txt";		# .. zielfile
my $SynFile						= "synonym-db/synonym-$lang.txt";
my $now_string					= localtime();


# stopword list initialisieren, später mittels datei machen
%StopWordList	= ( "einen" => undef ) ;

# großbuchstaben festlegen
%BigLetterList	= ( 
		"A" => undef,
		"B" => undef,		
		"C" => undef,		
		"D" => undef,		
		"E" => undef,		
		"F" => undef,		
		"G" => undef,		
		"H" => undef,		
		"I" => undef,		
		"J" => undef,		
		"K" => undef,		
		"L" => undef,		
		"M" => undef,		
		"N" => undef,		
		"O" => undef,		
		"P" => undef,		
		"Q" => undef,		
		"R" => undef,		
		"S" => undef,		
		"T" => undef,		
		"U" => undef,		
		"V" => undef,		
		"W" => undef,		
		"X" => undef,
		"Y" => undef,		
		"Z" => undef,		
) ;

# src file einlesen
open( RH, "<$SrcFile" ) or die "sudden flaming death\n";
	flock(RH, 2);
	$text = do { local( $/ ) ; <RH> } ;
close RH;

# synonym datei in den speicher einlesen
open(SYN,"<$SynFile") or die;
	flock(SYN,2);
	@SynonymContent = <SYN>;
close SYN;


@FullContent			= split(' ', $text);
my $TextWordCounter		= $#FullContent; 
my $FullContentArray	= &Main( \@FullContent );


# mod file schreiben
open( WH, ">$ModFile" ) or die "sudden flaming death\n";
	flock(WH, 2);

#	my $ModifiedText = "";
	for ( @{$FullContentArray} ) {	# todo: content ordnen: sprich nach 5 sätzen ( eg nach 5 .) ein \n schreiben
		print WH " " . $_;
#		$ModifiedText .= $_ . " ";
	};

#	my $sentences	= get_sentences($ModifiedText);	
#	for ( @{$sentences} ) {
#		print WH "$_\n";
#	};

close WH;

my $now1_string		= localtime;
my $ModifiedText	= sprintf("%.4f", $ModifiedWordcounter / $TextWordCounter ) * 100;


print "#################### ContentCleaner ####################\n";
print "#### \t [$ModifiedText%] modified Text\n";
print "#### \t [$ModifiedWordcounter] modified Words \n";
print "#### \t [$TextWordCounter] whole Words \n";

	print "#### \t Transformations used: ";
	if ( $UsedString == 1 ) {
		print "[StringManipulation()] ";
	};
	if ( $UsedTextBlowup == 1 ) {
		print "[TextBlowup()] ";
	};
	if ( $UsedSynonym == 1 ) {
		print "[SynonymManipulation()] ";
	};
	if ( $UsedZahl2Word == 1 ) {
		print "[Zahl2Wort()] ";
	};
	if ( $UsedGrundform == 1 ) {
		print "[GrundformManipulation()] ";
	};
	if ( $UsedTypingError == 1 ) {
		print "[TypingErrors()] ";
	};
	print "\n";

print "#### \t Beg: $now_string \n";
print "#### \t End: $now1_string \n";
print "#### \t Worked: $MainKeyWord	| $SrcFile | $SynFile\n";
print "#########################################################\n";

exit(0);


########
### Main(): rufe alle wichtige funktionen geordnet auf

sub Main(){

	my $FullContent		= shift;

	$FullContent		= &SynonymManipulation( $FullContent )		if ( $UseSynonym == 1 );			### SynonymManipulation(): zufälliges ersetzen von deutschen wörtern mit einem zufälligem synonym zum ursprünglichen wort	
	$FullContent		= &TextBlowup( $FullContent )				if ( $UseTextBlowup == 1 );		### TextBlowup(): füge satzkombinationen zum array hinzu
	$FullContent		= &ZahlManipulation( $FullContent )			if ( $UseZahl2Word == 1 );			### ZahlManipulation(): ersetzen von zahlen mit den entsprechenden namen für die zahlen
	$FullContent		= &StringManipulation( $FullContent )		if ( $UseString == 1 );			### StringManipulation(): buchstaben, zeichen, wörter spiegeln, vertauschen, löschen, ersetzen ...
	$FullContent		= &GrundformManipulation( $FullContent )	if ( $UseGrundform == 1 );			### GrundformManipulation(): reduzierung von deutschen wörtern auf ihre grundform
	my $TypingErrors	= &TypingErrors( $MainKeyWord )				if ( $UseTypingError == 1 );		### TypingErrors(): falschschreibung des aktuellen hauptkeywortes
	# print Dumper( $TypingErrors );
	
	return $FullContent;

}; # Main()




###########
### SynonymManipulation(): zufälliges ersetzen von deutschen wörtern mit einem zufälligem synonym zum ursprünglichen wort

sub SynonymManipulation(){

	my $FullContentArray	= shift;
	$UsedSynonym			= 1;
	my $UpperFlag			= 0;
	
	for ( @{$FullContentArray} ) {
		
		# $CurrentWordCount++;

		my $WordToModify		= $_;
		my $WordToModifyCopy	= $_;

		my $WordToModifyTemp	= $_;
		$WordToModifyTemp		=~ s/[;,?!.(){}&%$"\]\[]//g;	# ersetze alle ungewollten zeichen mit ""

		# benutze die schnelle funktion grep case-insensetive um nach dem aktuellen wort $WordToModifyTemp in der synonymdatenbank @SynonymContent nachzuschauen und das zu modifizierende wort länger als 5 zeichen ist
		my @grepNames			= grep( /$WordToModifyTemp/i, @SynonymContent) if length $WordToModifyTemp >= 5;
		
		
		# hole einen zufälligen dateneintrag eg "test,test2,test3" aus unserem synonym flatfile herraus
		my $SynTempEntrys		= @grepNames[ int(rand($#grepNames)) ];
		my $strlen				= length $SynTempEntrys;
		
		# wenn $strlen > 0 bedeutet, dass mittels grep ein match gefunden wurde
		# sprich, fahre fort: wenn aktuelles wort kein stopwort ist, ein match für ein synonym gefunden wurde, und das aktuelle wort nicht dem hauptkeyword entspricht

		if ( !exists $StopWordList{$WordToModifyTemp} && $strlen > 0 && $MainKeyWord !~ /$WordToModifyTemp/i ) {
		
			# hole die synonym einträge aus $SynTempEntrys und speichere sie in einem neue array
			my @SynTempEntrys = split("," , $SynTempEntrys );	
			
			# hole ein zufälligen eintrag aus unserem temporären @SynTempEntrys array
			# my $SynReplace	= @SynTempEntrys[ 0 ];	# hole den ersten eintrag aus dem array

			my $SynReplace	= @SynTempEntrys[ int(rand($#SynTempEntrys)) ];
			$SynReplace		=~ s/[;,?!.(){}&%$"\]\[]//g;

			# wenn das aktull ausgesuchte synonym mit dem aktuell zu ersetzenden wort übereinstimmt, dann versuche eine neues, zufälliges zu holen
			# später rekursive funktione einbauen

			if ( $SynReplace eq $WordToModifyTemp || $SynReplace =~ /$WordToModifyTemp/i ) {
				#	print "!!! [$WordToModifyTemp] Ersetzungsstring und Wort sind gleich!\n";
				$SynTempEntrys	= @grepNames[ int(rand($#grepNames)) ];
				@SynTempEntrys	= split("," , $SynTempEntrys );
				$SynReplace		= @SynTempEntrys[ int(rand($#SynTempEntrys)) ];
				#	print "[$SynReplace] -> neues synonym\n ";
			};

			#	my $ModifiedText = sprintf("%.4f", $ModifiedWordcounter / $TextWordCounter ) * 100;
			#	print "[$ModifiedText %] Modified Text - [$CurrentWordCount] Current Word / [$ModifiedWordcounter / $TextWordCounter] Modified Words\n";

			#	print "\tErsetze [$WordToModifyTemp] mit $SynReplace\n";
			#	print "Alter Satz: $_\n";

			
			# prüfe das zu ersetzende Wort auf Großschreibung des ersten Buchstabens, und manipuliere den content entsprechend
			# later: prüfe, ob das genommene Synonym auch ein Substantiv ist
			my @Letters = split('', $WordToModify);
			if ( exists $BigLetterList{$Letters[0]} ) {
				# setze das flag, das beim nächsten mal, der Wert in $_ groß am Anfang geschrieben werden soll
				$UpperFlag = 1;
			};


			# führe die eigentliche Wortersetzung durch
			$_ =~ eval { s/$WordToModify/$SynReplace/igc } ;


			# schreibe das nächste wort groß, nachdem zuletzt ein satzende festgestellt wurde
			if ( $UpperFlag == 1 ) {
				
				$UpperFlag = 0;
				$_ = ucfirst($_);

			}; # if ( $UpperFlag == 1 ) {


			# füge Satzzeichen wieder mit ein!
			if ( $WordToModifyCopy =~ /\./ ) {
				$_ = $_ . ".";
				$UpperFlag = 1;
			} elsif ( $WordToModifyCopy =~ /\,/ ) {
				$_ = $_ . ",";
			} elsif ( $WordToModifyCopy =~ /\;/ ) {
				$_ = $_ . ";";
			} elsif ( $WordToModifyCopy =~ /\!/ ) {
				$_ = $_ . "!";
				$UpperFlag = 1;
			} elsif ( $WordToModifyCopy =~ /\?/ ) {
				$_ = $_ . "?";
				$UpperFlag = 1;
			};

			# füge newlines hinzu: funzt nicht
			if ( $WordToModify =~ /\n/ ) {
				$_ .= "\n";
			};

			#	print "Neuer Satz: $_\n";

			$ModifiedWordcounter++;
			
		}; # if ( $strlen > 0 ) {

	
	#	my $random = rand();
	#	if ( $random > 0.975 ) {
	#		
	#		my $old		= $_;
	#		my $result;
	#		eval {
	#			$result	= Lingua::DE::Wortschatz::use_service('T', $_);
	#		};
	#		my @lines	= $result->hashrefs();
	#		my $String	= @lines[ int(rand(@lines)) ] ;
	#		$_			= $String->{Synonym} if ( length($String->{Synonym}) > 1 );		
	#		print "Old: $old --- New: $_\n";
	#
	#	};

	}; # for ( @FullContentArray ) {

	return $FullContentArray;

}; # sub SynonymManipulation(){



###########
### ZahlManipulation(): ersetzen von zahlen mit den entsprechenden namen für die zahlen

sub ZahlManipulation(){

	my $FullContentArray	= shift;
	$UsedZahl2Word			= 1;

	# zahl in wort umwandeln, wenn quellzeichen eine zahl ist
	for ( @{$FullContentArray} ) {

		if ( $_ =~ /\d/ && $lang == "de" ) {
			
			my $Copy = $_;
			#	print "old: $_\n";
		
			$_ = Zahl2Wort( $_ );
						
			if ( $Copy =~ /\./ ) {
				$_ = $_ . ".";
			} elsif ( $Copy =~ /\,/ ) {
				$_ = $_ . ",";
			} elsif ( $Copy =~ /\;/ ) {
				$_ = $_ . ";";
			} elsif ( $Copy =~ /\!/ ) {
				$_ = $_ . "!";
			} elsif ( $Copy =~ /\?/ ) {
				$_ = $_ . "?";
			};

			# im fehlerfall, restauriere den wert von $Copy als aktuellen wert von $_
			if ( $_ eq "null") {
				$_ = $Copy;
			} else {
				$ModifiedWordcounter++;
			};

			#	print "new: $_\n";

		} elsif ( $_ =~ /\d/ && $lang == "en" ) {

			my $Copy = $_;
			#	print "old: $_\n";
		
			$_ = Number2Word( $_ );
						
			if ( $Copy =~ /\./ ) {
				$_ = $_ . ".";
			} elsif ( $Copy =~ /\,/ ) {
				$_ = $_ . ",";
			} elsif ( $Copy =~ /\;/ ) {
				$_ = $_ . ";";
			} elsif ( $Copy =~ /\!/ ) {
				$_ = $_ . "!";
			} elsif ( $Copy =~ /\?/ ) {
				$_ = $_ . "?";
			};

			# im fehlerfall, restauriere den wert von $Copy als aktuellen wert von $_
			if ( $_ eq "zero") {
				$_ = $Copy;
			} else {
				$ModifiedWordcounter++;
			};
			
		}; # } elsif ( $_ =~ /\d/ && $lang == "en" ) {

	}; # for ( @{$FullContent} ) {

	return $FullContentArray;

}; # sub ZahlManipulation(){



############
### TextBlowup(): füge satzkombinationen zum array hinzu

sub TextBlowup(){
	
	my $FullContentArray	= shift;
	my $TextBlowupCounter	= 0;
	$UsedTextBlowup			= 1;

	for ( @{$FullContentArray} ) {
		
		# random wert von 0.9995 führt bei einem text von 26K zu 0-2 veränderungen
		if ( rand() > 0.9995 ) {
			$TextBlowupCounter++;
			my @alpha_sorted = ();									# lege neues array an

			for (my $i=0; $i<=int(rand(20))+7; $i++ ) {				# für 0 bis rand(30) fülle ein das neue array @alpha_sorted mit zufälligen werten aus dem arrays @FullContent
				my $randomString	= lc @FullContent[ int(rand($#FullContent)) ];
				$randomString		=~ s/[;,?!.(){}&%$"\]\[]//g;
				push(@alpha_sorted, $randomString );
			};
		
			@alpha_sorted	= sort { $b cmp $a } (@alpha_sorted);	# führe  sort aus ...
					
			# later: splice the content of @alpha_sorted as a string somewhere in the rand(middle) of @FullContent


			# ... und schaufele die geordneten daten wieder ans ende von  @FullContent
			my $RandCount = 0;
			for (@alpha_sorted) {
				if ( $RandCount == 0 ) {
					push(@{$FullContentArray}, ucfirst $_);		# das erste zeichen muss gross gemacht werden -> Satzbeginn
				} else {
					push(@{$FullContentArray}, $_);		
				}; # if ( $RandCount == 0 ) {
				$RandCount++;
			}; # for (@alpha_sorted) {

			push(@{$FullContentArray}, ".");					# Satzende -> Punkt setzen !

			$ModifiedWordcounter += $#alpha_sorted;
			print "[$TextBlowupCounter] TextBlowup @alpha_sorted.\n";

		#	sleep 3;
		#	print @{$FullContentArray};
		#	exit;

		}; # if ( $random > 0.95 ) {

	}; # for ( @FullContent ) {

	return $FullContentArray;

}; # sub TextBlowup(){


###########
### Zahl2Wort(): wandele eine zahl in ein deutsches wort um

sub Zahl2Wort(){

	my $zahl = shift;
	my $text = Lingua::DE::Num2Word::num2de_cardinal( $zahl );
	if ( length ($text) >= length ($zahl) ) {
		return $text;
	} else {
		return $zahl;
	};

}; # sub Zahl2Wort(){


###########
### Number2Word(): wandele eine zahl in ein englisches wort um

sub Number2Word(){

	my $zahl = shift;
	my $text = Lingua::EN::Inflect::NUMWORDS($zahl);
	if ( length ($text) >= length ($zahl) ) {
		return $text;
	} else {
		return $zahl;
	};

}; # sub Number2Word(){

###########
### TypingErrors(): falschschreibung des aktuellen hauptkeywortes

sub TypingErrors(){

	my $Keyword			= shift;
	$UsedTypingError	= 1;

	### wörter falsch schreiben 
	my $ldt = Lingua::DE::TypoGenerator->new();
	my @typos = $ldt->typos($Keyword);

	return \@typos;

}; # sub TypingErrors(){



############
### GrundformManipulation(): reduzierung von deutschen wörtern auf ihre grundform

sub GrundformManipulation(){
	
	my $FullContentArray	= shift;
	$UsedGrundform			= 1;	

	for ( @{$FullContentArray} ) {
		
		my $random = rand();

		if ( $random > 0.5 ) {
			$_ = Text::German::reduce($_);
			$ModifiedWordcounter++;
		}; # if ( $random > 0.5 ) {

	}; # for ( @FullContentArray ) {

	return $FullContentArray;

}; # sub GrundformManipulation(){





############
### StringManipulation(): buchstaben, zeichen, wörter spiegeln, vertauschen, löschen, ersetzen ...


sub StringManipulation(){
	
	my $FullContentArray	= shift;
	$UsedString				= 1;
	

	for ( @{$FullContentArray} ) {


		# für jede Anweisung wird eine neue zufällige Zahl erstellt
		

		###
		# Erste Buchstaben groß/klein schreiben
		###

		my $random = rand();
		if ( $random > 0.4 && $random < 0.5 ) {
			$_ = ucfirst();		# zufällig ersten Buchstaben groß schreiben
		} elsif ( $random > 0.5 && $random < 0.6 ) {
			$_ = lcfirst();		# zufällig ersten Buchstaben klein schreiben
		};


		###
		# Wort groß/klein schreiben
		###

		my $random = rand();
		if ( $random >= 0.3 && $random <= 0.4 ) {
			$_ = uc();		# zufällig Wort groß schreiben
		} elsif ( $random >= 0.7 && $random <= 0.8 ) {
			$_ = lc();		# zufällig Wort klein schreiben
		};


		###
		# zufälliges Zeichen einfügen
		###

		my $random			= rand();
		my $replaceString	= @characters[ rand(@characters) ] ;
		
		if ( $random < 0.09 ) {
			substr($_, rand(length), 0 ) = "$replaceString";
		};
		
		
		###
		# zufällige Nummer einfügen
		###

		my $random			= rand();
		my $replaceString	= @numbers[ rand(@numbers) ] ;
		
		if ( $random < 0.09 ) {
			substr($_, rand(length), 0 ) = "$replaceString";
		};


		###
		# zufälligen Buchstaben einfügen
		###

		my $random			= rand();
		my $replaceString	= @letters[ rand(@letters) ] ;
		
		if ( $random < 0.09 ) {
			substr($_, rand(length), 0 ) = "$replaceString";	
		};


		###
		# Wörter zufällig verdoppeln
		###

		my $random = int(rand(30));
			
		if ( $random == 5 ) {
			$_ .= $_;
		};


		###
		# Wörter zufällig verdoppeln und das vedoppelte spiegeln
		###

		my $random = int(rand(30));
			
		if ( $random == 6 ) {
			$_ .= reverse $_;
		};


		###
		# Wörter zufällig spiegeln
		###

		my $random = int(rand(30));
			
		if ( $random == 7 ) {
			$_ = reverse $_;
		};


		###
		# zeichen aus einem Wort zufällig löschen
		###

		my $random	= rand();
		my $length  = length($_);
	
		if ( $length > 0 ) {
		
			if ( $random < 1/$length ) {
				substr($_, rand(length), rand(length) ) = "";
			}; # if ( $random < 1/$length ) {
		
		}; # if ( $length > 0 ) {
		
		###
		# Deutsche Umlaute zufällig ersetzen
		###

		my $random = int(rand(30));
			
		if ( $random == 1 ) {
			eval {
				$_ = to_ascii($_) if ( length($_) > 3 );
			};
		};


		###
		# Wort zufällig mit unsinnigen zeichen füllen
		###

		my $random	= rand();
		my $length  = length($_);
		
		if ( $length > 0 ) {

			if ( $random < 3/length ) {

				my $cp = $_;
				$_  = "";

				for ( length($cp) ) {
					
					my $random = int(rand(127));
					$_ .= chr($random);

				}; # for
			}; # if ( $random
		}; # if ( $length > 0 ) {
	
	}; # for ( @FullContent ) {

	return $FullContentArray;

}; # sub StringManipulation(){


# my $LevenshteinDistance = fastdistance($WordToModifyTemp, $SynEntry) if length $WordToModifyTemp > 3;
			# print "$WordToModifyTemp MATCH $SynEntry\n";
			# if ( $WordToModifyTemp =~ /$SynEntry/i && length $WordToModifyTemp > 3 && $LevenshteinDistance <= 2 ) {
			

# bringe die synonyme in ein hash ein
#for ( @SynonymContent ) {
#	# my @SynTempEntrys = split("," , $_ );
#	for ( split("," , $_ ) ) {
#		chomp;
#		# print "adde $_\n";
#		$SynonymContent->{ lc($_) } = undef;
#	}; # for ( @SynTempEntrys ) {
#}; # for ( @SynonymContent ) {

#print Dumper($SynonymContent); 
#exit;


1;