<?

class wordspace {

	private $processing_callbacks;

	private $sql;

	public $completed;

	private $statement;

	public function __construct() {
		$this->sql = $data->main_instance;
		$this->statement = $data->statement;
		$this->processing_callbacks = [];
		$this->all_vectors = $object->create();
		$this->words = $object->create();
		$this->completed = $object->create();

		$object->get_word = function($value/*, $no_duplicates*/) {
			$value = $object->strings->lower($object->strings->trim($value));
			if($object->isset($this->wordspace->words[$value])) {
				/*if($object->isset($no_duplicates)) {
					return NULL;
				}*/
				return $this->wordspace->words[$value];
			} else {
				$word = new word($value);
				if($word->successfully_set) {
					$this->wordspace->words[$value] = $word;
				}
				return $word;
			}
			return NULL;
		};
	}

	public function append_processing_callback($callback) {
		$this->processing_callbacks[] = $callback;
	}

	public $words = NULL;

	public function begin_batch_processing($set_tags) {
		$object->log('befin batch processing');
		$this->words = $object->create();

		foreach($set_tags as $tag => $vector_value) {
			$tag = $object->strings->lower($object->strings->trim($tag));
			$this->all_vectors[$tag] = $vector_value; 
			/*$this->words[$tag] = new word($tag);*/
			$object->get_word($tag);
			$object->log('set word', $tag);
		}

		/*$query = 'SELECT * FROM tag_vectors WHERE type = ? OR type = ? OR type = ? or type = ? or type = ? ORDER BY RANDOM() LIMIT 50 ';
		$rows = $this->sql->get_rows($query, [
			'nouns',
			'adjectives',
			'prepositions',
			'verbs',
			'adverbs'
		]);

		foreach($rows as $row) {
			$this->words[$row['tag_value']] = new word($row['tag_value']);
		}*/
	}

	public function perform_batch_processing($last_callback) {
		$this->processing_callbacks = [];
		$object->log('perform batch processing');
		foreach($this->words as $word) {
			$definition_processing_instance = new definition_processing($word);
			$definition_processing_instance->process();
		}

		$object->log('length');
		$object->log($this->processing_callbacks->length);

		$callback_sorted = $object->randomize_list($this->processing_callbacks);


		/*foreach($callback_sorted as $callback) {
			$callback(function() {
				$object->log('completed callback 1');
			});
		}*/


		/*while($this->processing_callbacks->length > 0) {
			$random_index = $math->random_int($this->processing_callbacks->length);

			if($random_index >= $this->processing_callbacks->length) {
				$random_index = 0;
			}

			$callback_sorted[] = $object->splice($this->processing_callbacks, $random_index, 1)[0];
		}*/

		$set_callback_count = $callback_sorted->length;

		$res = [];

		$self = $this;

		$set_callback_last = function() {
			$object->log('begin store');
			$object->denormalize($self->all_vectors);
			foreach($self->words as $word) {
				$word->store();
				$object->log('stored: '.$word->get_tag_value());
			}

			$object->log('called store and continue');
			$object->log($last_callback);
			$last_callback($self->all_vectors);
		};

		$iterations = 1;

		$set_callback_count = $set_callback_count * $iterations;


		$completed_callback = function() {
			$res[] = 1;
			if($res->length == $set_callback_count) {

				

				$object->once($set_callback_last);
			}
		};

		$iterator = 0;
		while($iterator < $iterations) {
			foreach($callback_sorted as $callback) {
				$callback($completed_callback);
			}
			$object->log('iteration: '.$iterator);
			$iterator = $iterator + 1;
		}

		$object->log('completed');


	}

	public $learning_rate = 50;

	public $all_vectors;
}

class word {

	private $value;

	private $values;

	public $sub_words = NULL;

	private $type = NULL;

	private $vector_value = NULL;

	public $wordspace;

	public $parent;

	public $successfully_set = false;

	public function __construct($value) {
		$object->log('construct word '.$value);
		/*$this->parent = $parent;*/
		$this->wordspace = $object->wordspace;
		$this->sql = $data->main_instance;
		$this->statement = $data->statement;

		/*if($set_vector_value != NULL) {
			$this->vector_value = $set_vector_value;
		}*/

		$value = $object->strings->trim($value);
		$value = $object->strings->lower($value);
		if($object->strings->strpos($value, ' ') != (-1)) {
			$split = $object->strings->split($value, ' ');
			$object->log('split: '.$object->toJSON($split));
			$this->value = $value;
			$this->values = $split;
			$this->set_item($value);
			$this->set_items();
		} else {
			$this->values = NULL;
			$this->value = $value;
			$this->set_item($this->value);

			$query = 'SELECT * FROM hyponyms WHERE LOWER(hyponyms) = ?';
			$hyponyms = $this->sql->get_rows($query, [$value]); /*.'%'*/
			$object->log($object->toJSON($hyponyms));
			if($hyponyms->length > 0) {
				$this->values = [];
			}
			foreach($hyponyms as $hyponym) {
				$lemma = $object->strings->lower($hyponym['lemma']);
				if($object->strings->strpos($lemma, ' ') == (-1)) {
					$object->log($lemma);
					if($lemma != $value) {
						$this->values[] = $lemma;
					}
				}
			}
			$object->log('hyponyms');
			$object->log($this->values);
			if($this->values != NULL && $this->values->length > 0) {
				$object->log('call set items');
				$object->log($object->toJSON($this->values));

				$this->set_items();
			}
			$object->log('after call to set items');

		}

	}

	public function store() {

		/**/

		/*$query = 'SELECT COUNT(*) FROM tag_vectors WHERE LOWER(tag_value) = ?';
		$count = $this->sql->get_row($query, [$this->value])['count'];

		if($count > 0) {*/
		if($this->vector_value != NULL) {
			$query = 'UPDATE tag_vectors SET vector_value = ?, rd1 = 1 WHERE LOWER(tag_value) = ?';
			$this->sql->execute($query, [$object->toJSON($this->vector_value), $this->value]);
		} else {
			$object->log('vector is null: '.$this->value);
		}
		/*}*//* else {
			$v = [
				'vector_value' => $object->toJSON($this->vector_value),
				'tag_value' => $this->value
			];

			$insert = $this->statement->generate($v, 'tag_vectors');
			$this->data->_($insert, $v);
		}*/
	}

	private $sql;

	private $statement;

	public function get_vector_value() {
		return $this->vector_value;
	}

	public function get_type() {
		return $this->type;
	}

	public function get_tag_value() {
		return $this->value;
	}

	public function set_item($value) {
		$object->log('set item: '.$value);
		$query = 'SELECT * FROM tag_vectors WHERE LOWER(tag_value) = ?';
		$row = $this->sql->get_row($query, [$value]);

		if($row != NULL) {
		
			/*$v = [
				'word' => $value,
				'vector_value' => $this->parent->get_vector_value(),
				'type' => $this->parent->get_type()
			];


			$insert = $data->statement->generate($v, 'prepositions');
			$data_instance->_($insert, $v);*/
		
			$this->type = $row['type'];

			$object->log($object->toJSON($row));

			if($object->isset($this->wordspace->all_vectors[$value])) {
				$this->vector_value = $this->wordspace->all_vectors[$value];
			} else {
				$this->vector_value = $object->fromJSON($row['vector_value']);
				$this->wordspace->all_vectors[$value] = $this->vector_value;
			}
			/*if(!$object->isset($this->wordspace->words[$value])) {
				$this->wordspace->words[$value] = $this;
			}*/
			/*$this->vector_value = $object->fromJSON($row['vector_value']);*/
			$this->successfully_set = true;

		}
		$object->log('set');
	}

	public function set_items() {
		$this->sub_words = [];

		$total_vector = NULL;

		$m_vector = new m_vector();
		if($this->values != NULL) {
			foreach($this->values as $value) {
				$value = $object->strings->trim($value);
				$object->log('sub word: '.$value);
				$word = NULL;
				if($object->isset($this->wordspace->words[$value])) {
					$object->log('exists');
					$word = $this->wordspace->words[$value];
				} else {
					$object->log('new');
					/*$word = new word($value);*/
					$word = $object->get_word($value);
				}


				if($word != NULL && $word->successfully_set && $word->get_vector_value() != NULL) {
					if($object->isset($word)) {
						$this->sub_words[] = $word;
						if($total_vector === NULL) {
							if($word->get_vector_value() != NULL) {
								$total_vector = $word->get_vector_value();
							}
						} else {
							if($word->get_vector_value() != NULL) {
								$total_vector = $m_vector->add_vector($total_vector, $word->get_vector_value());
							}
						}
					}
				}
			}
			if($total_vector != NULL) {
				$total_vector = $m_vector->normalize($total_vector);

				$this->apply_equality($total_vector);
			}
		}
	}

	public function set_vector($vector_value) {
		/*if($vector_value === true) {

		}*/
		$this->vector_value = [...$vector_value];
		$this->wordspace->all_vectors[$value] = $this->vector_value;
	}

	public function get_value() {
		return $this->value;
	}

	public function apply_equality($second_vector, $negative=false) {
		$object->log('apply equality');
		$m_vector = new m_vector();

		if($second_vector != NULL) {
			$object->log($object->toJSON([$second_vector, $this->vector_value]));
			/*$second_vector = $equality_word_item->get_vector_value();*/
			if($negative) {
				$second_vector = $m_vector->reverse_vector($second_vector);
			}

			if($this->vector_value == NULL) {
				if($second_vector != NULL) {
					$this->set_vector($second_vector);
				}
				return false;
			}

			foreach($this->vector_value as $index => $value) {
				$this->vector_value[$index] = $this->vector_value[$index] + ($second_vector[$index] / $object->wordspace->learning_rate);
			}
		}
		return NULL;
		/*$this->vector_value = $m_vector->normalize($this->vector_value);*/
	}
}


class word_processing {

	public $word;

	protected $sql;

	protected $statement;

	public function __construct($word) {
		$this->word = $word;
		$this->sql = $data->main_instance;
		$this->statement = $data->statement;
	}

	public function get_equalities() {
		$query = 'SELECT * FROM words_equalities WHERE word = ?';
		$equalities = $this->sql->get_rows($query, [$this->word->get_value()]);
		return $equalities;
	}

	public function get_antonyms() {
		$query = 'SELECT * FROM antonyms WHERE word = ?';
		$antonyms = $this->sql->get_rows($query, [$this->word->get_value()]);

		return $antonyms;
	}

	public function get_references() {
		$query = 'SELECT * FROM statements WHERE value LIKE ?';
		$references = $this->sql->get_rows($query, ['%'.$this->word->get_value().'%']);

		return $references;
	}

	/*public function get_parents() {
		$query = 'SELECT * FROM ';
	}*/
}

class definition_processing extends word_processing {

	public function process() {
		$object->log('process');
		$object->log($this->word);

		$self_vector = $this->word->get_vector_value();
		$m_vector_instance = new m_vector();

		if($self_vector == NULL) {
			$object->log('self vector is null: '.$word->get_tag_value());
		}

		$object->log($object->toJSON($self_vector));
		$object->log($this->word->get_tag_value());
		if($this->word->sub_words != NULL) {
			$object->log('has sub words');
			$object->log($this->word->sub_words->length);
			foreach($this->word->sub_words as $sub_word) {
				$inner = function($sub_word, $self) {
					$this->word->wordspace->append_processing_callback(function($set_callback) {
						$object->log('apply sub-word processing callback');
						$subtraction = NULL;

						$object->log($self);
						$object->log($self->word);
						$object->log($self->word->sub_words);

						$tag_value = $self->word->get_tag_value();

						if(!$object->isset($object->wordspace->completed[$tag_value])) {
							$object->wordspace->completed[$tag_value] = true;
							foreach($self->word->sub_words as $other_word) {
								$object->log('other word');
								$object->log($other_word);
								if(!$object->equals($sub_word, $other_word)) {
									$object->log('not equal');
									if($other_word->get_vector_value() != NULL) {
										if($subtraction === NULL) {
											$subtraction = $other_word->get_vector_value();
										} else {
											$subtraction = $m_vector_instance->add_vector($subtraction, $other_word->get_vector_value());
										}
									}
								}
							}
							$object->log($object->toJSON($self_vector));

							if($subtraction != NULL) {
								$object->log($object->toJSON($subtraction));

								$set_vector = $m_vector_instance->subtract_vector($self_vector, $m_vector_instance->normalize($subtraction));
								$object->log($object->toJSON($set_vector));

								$object->log('call apply equality');

								$sub_word->apply_equality($set_vector);

								$object->log('call set callback');
							}
							$set_callback();
						} else {
							$set_callback();
						}
					});
				};
				$inner($sub_word, $this);
				$object->log($sub_word->get_tag_value());
			}
		}

		$equalities = $this->get_equalities();

		$object->log('begin equalities');
		$object->log($object->toJSON($equalities));

		foreach($equalities as $equality) {
			$inner = function($equality, $self) {
				$this->word->wordspace->append_processing_callback(function($set_callback) {
					$object->log('apply eqality processing callback');
					
					$tag_value = $self->word->get_tag_value();

					if(!$object->isset($object->wordspace->completed[$tag_value])) {
						$object->wordspace->completed[$tag_value] = true;

						$antonym = $equality['equality'];
						$antonyms = [];
						if($object->strings->strpos($antonym, ';') != (-1)) {
							$antonyms = $object->strings->split($antonym, ';');
						} else {
							$antonyms[] = $antonym;
						}

						foreach($antonyms as $antonym) {
							if($object->strings->strpos($antonym, '|') != (-1)) {
								$antonym = $object->strings->split($antonym, '|')[0];
							}
							$word_equality = $object->get_word($antonym);
							if($word_equality != NULL) {
								/*$word_eqality = new word($antonym);*/

								$self->word->apply_equality($word_equality->get_vector_value());
							}
						}

						$object->log('call set callback');
						$set_callback();
					} else {
						$set_callback();
					}
				});
			};
			$inner($equality, $this);
		}

		$antonyms = $this->get_antonyms();

		$object->log('begin antonyms');
		$object->log($object->toJSON($antonyms));

		foreach($antonyms as $equality) {
			$inner = function($equality, $self) {
				$this->word->wordspace->append_processing_callback(function($set_callback) {
					$object->log('apply antonym processing callback');

					$tag_value = $self->word->get_tag_value();

					if(!$object->isset($object->wordspace->completed[$tag_value])) {
						$object->wordspace->completed[$tag_value] = true;
						$antonym = $equality['antonyms'];
						$antonyms = [];
						if($object->strings->strpos($antonym, ';') != (-1)) {
							$antonyms = $object->strings->split($antonym, ';');
						} else {
							$antonyms[] = $antonym;
						}

						foreach($antonyms as $antonym) {
							if($object->strings->strpos($antonym, '|') != (-1)) {
								$antonym = $object->strings->split($antonym, '|')[0];
							}

							$word_equality = $object->get_word($antonym);

							if($word_equality != NULL) {
								$self->word->apply_equality($word_equality->get_vector_value(), true);
							}
						}

						$object->log('call set callback');
						$set_callback();
					} else {
						$set_callback();
					}
				});
			};
			$inner($equality, $this);
		}
		$object->log('end antonyms');
	}

}

/*class disjunction_processing extends word_processing {

	public function process() {
		$references = $this->get_references();

		foreach($references as $reference) {
			$statement = $reference['value'];
			$split = $object->strings->split($statement, ' ');
		}
	}

}*/

?>