<?php


class WPML_Translation_Tree extends WPML_SP_User {

	private $taxonomy;
	private $tree = array();
	private $trid_levels;
	private $language_order;
	private $term_ids;

	public function __construct( &$sitepress, $element_type, $terms ) {
		if ( ! $terms ) {
			throw new InvalidArgumentException( 'No terms to order given given' );
		}
		parent::__construct( $sitepress );
		$this->taxonomy       = $element_type;
		$this->language_order = $this->get_language_order( $terms );
		$this->tree           = $this->get_tree_from_terms_array( $terms );
	}

	public function get_alphabetically_ordered_list() {
		$root_list              = $this->sort_trids_alphabetically( $this->tree );
		$ordered_list_flattened = array();
		foreach ( $root_list as $root_trid_group ) {
			$ordered_list_flattened = $this->get_children_recursively( $root_trid_group, $ordered_list_flattened );
		}

		return $ordered_list_flattened;
	}

	private function get_tree_from_terms_array( $terms ) {
		$trids     = $this->generate_trid_groups( $terms );
		$trid_tree = $this->parse_tree( $trids, false, 0 );

		return $trid_tree;
	}

	private function generate_trid_groups( $terms ) {
		$trids = array();
		foreach ( $terms as $term ) {
			$trids [ $term->trid ] [ $term->language_code ] = array(
				'ttid'    => $term->term_taxonomy_id,
				'parent'  => $term->parent,
				'term_id' => $term->term_id,
			);
			if ( isset( $term->name ) ) {
				$trids [ $term->trid ] [ $term->language_code ]['name'] = $this->get_unique_term_name( $term->name );
			}

			$this->term_ids[] = $term->term_id;
		}

		return $trids;
	}

	private function get_unique_term_name( $name ) {
		return uniqid( $name . '-' );
	}

	private function parse_tree( $trids, $root_trid_group, $level ) {
		$return = array();

		foreach ( $trids as $trid => $trid_group ) {
			if ( $this->is_root( $trid_group, $root_trid_group ) ) {
				unset( $trids[ $trid ] );
				if ( ! isset( $this->trid_levels[ $trid ] ) ) {
					$this->trid_levels[ $trid ] = 0;
				}
				$this->trid_levels[ $trid ] = max( array( $level, $this->trid_levels[ $trid ] ) );
				$return [ $trid ]           = array(
					'trid'     => $trid,
					'elements' => $trid_group,
					'children' => $this->parse_tree( $trids, $trid_group, $level + 1 ),
				);
			}
		}

		return empty( $return ) ? false : $return;
	}

	private function is_root( $children, $parent ) {
		$root = ! (bool) $parent;
		foreach ( $this->language_order as $c_lang ) {
			if ( ! isset( $children[ $c_lang ] ) ) {
				continue;
			}
			$child_in_lang = $children[ $c_lang ];
			if ( $parent === false ) {
				$root = ! ( $child_in_lang['parent'] != 0 || in_array( $child_in_lang['parent'], $this->term_ids ) );
				break;
			} else {
				foreach ( (array) $parent as $p_lang => $parent_in_lang ) {
					if ( $c_lang == $p_lang && $child_in_lang['parent'] == $parent_in_lang['term_id'] ) {
						$root = true;
						break;
					}
				}
			}
		}

		return $root;
	}

	private function sort_trids_alphabetically( $trid_groups ) {
		$terms_in_trids = array();
		$ordered_trids  = array();
		foreach ( $trid_groups as $trid_group ) {
			foreach ( $trid_group['elements'] as $lang => $term ) {
				if ( ! isset( $terms_in_trids[ $lang ] ) ) {
					$terms_in_trids[ $lang ] = array();
				}
				$trid                               = $trid_group['trid'];
				$term['trid']                       = $trid;
				$terms_in_trids[ $lang ][ $trid ][] = $term;
			}
		}

		$sorted = array();
		foreach ( $this->language_order as $lang ) {
			if ( isset( $terms_in_trids[ $lang ] ) ) {
				$terms_in_lang_and_trids = $terms_in_trids[ $lang ];
				$term_names              = array();
				$term_names_numerical    = array();
				foreach ( $terms_in_lang_and_trids as $trid => $terms_in_lang_and_trid ) {
					if ( in_array( $trid, $sorted ) ) {
						continue;
					}
					$term_in_lang_and_trid     = array_pop( $terms_in_lang_and_trid );
					$term_name                 = $term_in_lang_and_trid['name'];
					$term_names [ $term_name ] = $trid;
					$term_names_numerical []   = $term_name;
				}
				natsort( $term_names_numerical );
				foreach ( $term_names_numerical as $name ) {
					$ordered_trids [] = $trid_groups[ $term_names[ $name ] ];
					$sorted[]         = $term_names[ $name ];
				}
			}
		}

		return $ordered_trids;
	}

	private function get_children_recursively(
		$trid_group,
		$existing_list = array()
	) {
		$children = $trid_group['children'];
		unset( $trid_group['children'] );
		$existing_list [] = $this->add_level_information_to_terms( $trid_group );
		if ( is_array( $children ) ) {
			$children = $this->sort_trids_alphabetically( $children );
			foreach ( $children as $child ) {
				$existing_list = $this->get_children_recursively(
					$child,
					$existing_list
				);
			}
		}

		return $existing_list;
	}

	private function add_level_information_to_terms( $tridgroup ) {
		foreach ( $tridgroup['elements'] as $lang => &$term ) {
			$level   = 0;
			$term_id = $term['term_id'];
			while ( ( $term_id = wp_get_term_taxonomy_parent_id( $term_id, $this->taxonomy ) ) ) {
				$level ++;
			}
			$term['level'] = $level;
		}

		return $tridgroup;
	}

	private function get_language_order( $terms ) {
		$langs        = array();
		$default_lang = $this->sitepress->get_default_language();
		foreach ( $terms as $term ) {
			$term_lang = $term->language_code;
			if ( $term_lang === $default_lang ) {
				continue;
			}
			if ( isset( $langs[ $term_lang ] ) ) {
				$langs[ $term_lang ] += 1;
			} else {
				$langs[ $term_lang ] = 1;
			}
		}
		natsort( $langs );
		$return    = array_keys( $langs );
		$return [] = $default_lang;
		$return    = array_reverse( $return );

		return $return;
	}
}
