<?php

namespace Edi;

require_once(__DIR__.'/section.php');

class SectionInfo {
	var $schema;
	var $parentInfo;
	var $section;
}

class Message {
	var $section;
	
	static function loadSchema($schemaName) {
		$ediSchemaStr = file_get_contents(__DIR__."/$schemaName.json", TRUE);

		if ($ediSchemaStr !== FALSE)
			return json_decode($ediSchemaStr, TRUE);
			
		return NULL;
	}

	static function isEdiString(&$string) {
		return substr($string, 0, 4) == 'UNB+';
	}

	function parse(&$string, &$schema = NULL) {
		global $delimiters;

		if (!self::isEdiString($string))
			throw new \Exception('Not an EDI string.');

		$pos = 0;
		$error = FALSE;
		$endTag = NULL;
		$firstLoop = TRUE;
		$newSection = TRUE;

		$info = new SectionInfo();
		$info->schema = $schema;
		$info->parentInfo = NULL;
		$info->section = NULL;
		$topInfo = $info;

		try {
		while (TRUE) {
			$segment = $this->parseSegment($string, $pos);

			if (!$segment &&(!$endTag || !$info))
				break;
			
			if (!$segment ||($segment && !$info))
				throw new \Exception();

			if ($firstLoop) {			
				if ($segment->name != $info->schema['mainTag'])
					throw new \Exception();
			} else {
				for ($i = $info; $i; $i = $i->parentInfo)
				if (isset($i->schema['childs'][$segment->name])) {
					$info = new SectionInfo();
					$info->schema = $i->schema['childs'][$segment->name];
					$info->parentInfo = $i;
					$newSection = TRUE;
					break;
				}
			}

			if ($newSection) {
				$section = new Section();
				$section->name = $segment->name;
				$info->section = $section;
				
				if ($info->parentInfo) {
					$section->parent = $info->parentInfo->section;
					$section->parent->childs[$segment->name][] = $section;
				}

				if (isset($info->schema['endTag']))
					$endTag = $info;
					
				$newSection = FALSE;
			}
		
			if ($endTag && $endTag->schema['endTag'] == $segment->name) {
				$endTag->section->segments[] = $segment;
				$info = $endTag->parentInfo;
				
				for ($i = $info; $i; $i = $i->parentInfo)
				if (isset($i->schema['endTag'])) {
					$endTag = $i;
					break;
				}
			} else
				$info->section->segments[] = $segment;
			
			$firstLoop = FALSE;
		}} catch (\Exception $e) {
			throw new \Exception(sprintf('Parse error, something is wrong near "%s"',
				substr($string, $pos, 10)));
		}

		$this->section = $topInfo->section;
	}

	function parseSegment(&$string, &$pos) {
		$empty = TRUE;
		$values = [];
	
		while (TRUE) {
			if (!isset($string{$pos}))
				return NULL;
		
			if (in_array($string{$pos}, ['+', ':', '\''])) {
				if (!$empty) {
					$values[] =
						trim(substr($string, $start, $pos - $start));
					$empty = TRUE;
				}
			}
			elseif ($empty) {
				$start = $pos;
				$empty = FALSE;
			}
			
			if ($string{$pos} === '\'')
				break;

			$pos++;
		}
		
		$pos++;
	
		$segment = new Segment();
		$segment->name = $values[0];
		$segment->values = $values;
		return $segment;
	}
}