# Copyright 1999-2016. Parallels IP Holdings GmbH. All Rights Reserved.
package Parser;

use Logging;

sub makeFileReader
{
	my ($filename, $doNotFail) = @_;
	local (*INPUT);

	unless (-T $filename) {

		if (defined($doNotFail)) {
			Logging::debug("Error: file '$filename' is not a text file");
		} else {
			Logging::error("Error: file '$filename' is not a text file");
		}

		return undef;
	}

	unless (open(INPUT,"<$filename")) {

		binmode INPUT;
		if (defined($doNotFail)) {
			Logging::debug("Error: unable to open file '$filename' to read");
		} else {
			Logging::error("Error: unable to open file '$filename' to read");
		}

		return undef;
	}

	my $input = *INPUT;

	my $this = {
		'READ' => sub {
				my $a = <$input>;
				return $a;
			},

		'CLOSE' => sub { close($input); }

	};

	return $this;
}

sub makeArrayReader {
  my (@arr) = @_;
  my $size= 0;
  my $count = scalar(@arr);

  my $this={'READ'=>sub {
			  if($count == $size) {
				return undef;
			  }

			  return $arr[$count++];
			},
			'CLOSE'=>sub {
			}
		   };
  return $this;
}

sub makeParser {
  my $reader;
  my ($this,$ptrRows,$keySep,$valSep,$valKeySep,$comment,
      $typeRows,$maxIndex,$rowIndex);
  {
	$reader = shift;
	unless(defined($reader)) {
	  return undef;
	}

    my %args=(@_);
    $keySep = $args{'KEYSEPARATOR'};
    $valSep = $args{'VALUESEPARATOR'};
    $valKeySep = $args{'KEYVALUESEPARATOR'};
  }
  $this={'READER'=>sub {
		   if(@_){
			 $reader=shift;
		   }
		   return $reader;
		 },
		 'FILE'=>sub {
		   if(@_){
			 $reader = makeFileReader(@_);
		   }
		   return undef;
		 },
		 'KEYSEPARATOR'=>sub{
		   if(@_){
			 $keySep=shift;
		   }
		   return $keySep;
		 },
		 'VALUESEPARATOR'=>sub{
		   if(@_){
			 $valSep=shift;
		   }
		   return $valSep;
		 },
		 'KEYVALUESEPARATOR'=>sub{
		   if(@_){
			 $valKeySep=shift;
		   }
		   return $valKeySep;
		 },
		 'COMMENT'=>sub{
		   if(@_){
			 $comment=shift;
		   }
		   return $comment;
		 },
		 'PARSE'=>sub{
		   my %args=(@_);

		   $keySep=$args{'KEYSEPARATOR'} if exists $args{'KEYSEPARATOR'};
		   $valSep=$args{'VALUESEPARATOR'} if exists $args{'VALUESEPARATOR'};
		   $valKeySep=$args{'KEYVALUESEPARATOR'} if exists $args{'KEYVALUESEPARATOR'};
		   $comment=$args{'COMMENT'} if exists $args{'COMMENT'};

		   $ptrRows = parseFile('reader'=>$reader,'keysep'=>$keySep,
								 'valsep'=>$valSep,'keyvalsep'=>$valKeySep,
								 'comment'=>$comment);
		   $typeRows = ref($ptrRows);
		   if($typeRows=~/ARRAY/){
			 $rowIndex=-1;
			 $maxIndex=scalar(@{$ptrRows})-1;
		   }
		   return $ptrRows;
		 },
		 'ROW'=>sub{
		   if($typeRows=~/HASH/){
			 my $key=shift;
			 return $ptrRows->{$key};
		   }elsif($typeRows=~/ARRAY/){
			 if($rowIndex<$maxIndex){
			   $rowIndex++;
			   return $ptrRows->[$rowIndex];
			 }else{
			   return undef;
			 }
		   }else{
			 return undef;
		   }
		 },
		};

  return $this;
}

sub makeFileParser {
  my $filename = shift;
  return makeParser(makeFileReader($filename), @_);
}

sub makeSafeFileParser {
  my $filename = shift;
  return makeParser(makeFileReader($filename, 'doNotFail'), @_);
}

sub makeStringParser {
  my $content = shift;
  return makeParser(makeArrayReader($content), @_);
}

sub parseFile {
  my %arg = (@_);
  my $reader = $arg{'reader'};
  my $keySep = $arg{'keysep'};
  my $valSep = $arg{'valsep'};
  my $valKeySep = $arg{'keyvalsep'};
  my $comment = $arg{'comment'};

  my ($ret,$key,$value,$reKey,$reValue,@values,$keyBlank,$valBlank,
     $vkey,$vvalue,$reKeyValue,$reComment);

  if($keySep){
    $ret = {};
    if(ref($keySep)=~/regexp/i || ($keySep eq ' ')){
      $reKey = $keySep;
    }else{
      $reKey = qr/$keySep/;
    }
  }else{
    $ret = [];
  }
  if($valSep){
    if(ref($valSep)=~/regexp/i || ($valSep eq ' ')){
      $reValue = $valSep;
    }else{
      $reValue = qr/$valSep/;
    }
  }
  if($valKeySep){
    if(ref($valKeySep)=~/regexp/i || ($valKeySep eq ' ')){
      $reKeyValue = $valKeySep;
    }else{
      $reKeyValue = qr/$valKeySep/;
    }
  }
  if($comment){
    if(ref($comment)=~/regexp/i){
      $reComment = $comment;
    }else{
      $reComment = qr/$comment/;
    }
  }

  while($_ = $reader->{'READ'}->()){
    chomp;
    next unless $_;
    if($comment && /$reComment/){
	  next;
    }
    if ($keySep){
      ($key,$value)=split($reKey,$_,2);
      if($key){
		$key=~s/^\s+//;
		$key=~s/\s+$//;
		if($valSep){

		  if ($valKeySep){
			$ret->{$key}={};
			foreach $value (split($reValue,$value)){
			  ($vkey,$vvalue)=split($reKeyValue,$value);
			  $vkey=~s/^\s+//;
			  $vkey=~s/\s+$//;
			  $ret->{$key}->{$value}=$vvalue;
			}
		  }else{
			push @{$ret->{$key}},split($reValue,$value);
		  }
		}else{
		  $value=~s/^\s+//;
		  $value=~s/\s+$//;
		  $ret->{$key}=$value;
		}
      }
    }else{
      if($valSep){
		push @{$ret},[split($valSep,$_)];
      }else{
		push @{$ret},$_;
      }
    }
  }

  $reader->{'CLOSE'}->();

  return $ret;
}


1;