<?php
 
/*
CREATE VIEW einkaufslisten_details (herstellungsid, ist_endprodukt, itemid, name, beschreibung, farbe, gewicht, volumen, geldwert, geldwert_overwrite, faerbbar, klassenname, int_0, int_1, volk, stackable, wearable) AS (SELECT her_herstellungsid, ist_endprodukt, bildid_1, name, einkaufsliste.beschreibung, farbe, gewicht, volumen, geldwert, geldwert_override, faerbbar, klassenname, int_0, int_1, hv_volkid, stackable, wearable FROM ((einkaufsliste LEFT JOIN herstellung ON einkaufsliste.herstellungsid = herstellung.her_herstellungsid)LEFT JOIN endprodukte ON herstellung.her_herstellungsid = endprodukte.end_herstellungsid) LEFT JOIN herstellung_voelker ON herstellung.her_herstellungsid = herstellung_voelker.hv_herstellungsid WHERE freigeschalten = 1 ORDER BY name, geldwert);
*/

class CharShopping {
	private $tiledatapath; 
	
	private $char; // reference to char object

	public $money = NULL;
	private $max_money = NULL;
	public $shoppinglist = array();
	public $farben = array();
	public $msg = NULL;

	
	public function __construct($money, $char) {
		assert(is_a($char, 'Char'));
		assert(is_int($money) AND $money > 0);	
		// $money ist basis Money (Kupfer); nicht aktuelles Vermögen
		global $MySQL, $settings;
    
    	$this->tiledatapath =$settings['mulpath']."tiledata.mul";
		
		$this->char = $char;		
		$this->money = $money;
		$this->max_money = $money;
		
		// Hole kaufbare Items aus der Datenbank
		$shoppinglist2 = $MySQL->fetch_complete(sprintf("SELECT * FROM server.einkaufslisten_details WHERE gruppe = 99 OR volk = %d OR herstellungsid IN (1898, 1899, 1900)", $this->char->get('volk')));
		
		// Id-Zuweisung und overwrite (override)-Behandlugng
		$readtiledata = false; // muss mal ausgewertet werden, wenn neue Items hinzukommen
		foreach($shoppinglist2 as $item) {
			// finde unvollständige Daten heraus
			if($item['itemid']==0 || strlen($item['name'])==0) {
				// TODO evtl. loggen (?)
				continue;
			}
			
			$this->shoppinglist[$item['herstellungsid']] = $item;
			if($item['geldwert_overwrite'] > 0) $this->shoppinglist[$item['herstellungsid']]['geldwert'] = $item['geldwert_overwrite'];
			// auch die Münzen speichern
			//$this->shoppinglist[$item['herstellungsid']]['gsk'] = $this->pimpMyMoney((int)$this->shoppinglist[$item['herstellungsid']]['geldwert']);
			
			if(strlen($this->shoppinglist[$item['herstellungsid']]['beschreibung'])>55) {
				$beschreibung = " (".substr($this->shoppinglist[$item['herstellungsid']]['beschreibung'], 0, 54)."...)";
			} elseif(strlen($this->shoppinglist[$item['herstellungsid']]['beschreibung'])>0) {
				$beschreibung = " (".$this->shoppinglist[$item['herstellungsid']]['beschreibung'].")";
			} else {
				$beschreibung = '';
			}
			$this->shoppinglist[$item['herstellungsid']]['list_beschreibung'] = $beschreibung;
			//var_dump($item['stackable']);
			if($item['stackable']!= 1 && $item['stackable'] != 0) {
				
				$readtiledata = true;
			} else {
				if($item['stackable'] == 1) {
					$this->shoppinglist[$item['herstellungsid']]['stackable'] = true;
				} else {
					$this->shoppinglist[$item['herstellungsid']]['stackable'] = false;
				}
				if($item['wearable'] == 1) {
					$this->shoppinglist[$item['herstellungsid']]['wearable'] = true;
				} else {
					$this->shoppinglist[$item['herstellungsid']]['wearable'] = false;
				}
			}
		}
		if($readtiledata == true) $this->readTileData();
		
		$this->farben = $MySQL->fetch_complete("SELECT * FROM server.startitem_farben ORDER BY bezeichnung");
		
		// Wenn ein Item nicht mehr auf der Shopping-Liste steht oder money-preise < 0 dann kicke das ganze (IException)
		// TODO: was ist, wenn es ein Item nicht mehr gibt?	
		$value = $this->calcValue();
		$this->money -= $value;
		if($this->money < 0) {
			$this->char->unequipped_items = array();
			for($i=1;$i<=24;$i++) {
				if($i==11 || $i==16 || $i == 21) continue;
				$this->char->equipped_items[$i] = NULL;
			}
			$this->money = $this->max_money; // auf das ursprüngliche Geld zurücksetzen
			$value = 0;
			$this->msg = "Die Preise der Startitems haben sich leider geändert. Du musst neu einkaufen!";
		}
		
		assert($money - $value >= 0);
	}
	
	public function colourize($farbe, $id) {
		// färbt einen Gegenstand im Rucksack mit der gewählten Farbe, wenn er färbbar ist
		assert(is_int($farbe) AND is_int($id));
		
		if(isset($this->char->unequipped_items[$id])===false) return(false);
		
		// prüfen, ob Gegenstand färbbar ist
		if($this->char->unequipped_items[$id]['her_info']['faerbbar']===false) {
			throw new IException("Diesen Gegenstand kannst du nicht färben!");
		}
		
		// Testen, ob die Farbe in der Liste der zulässigen Farben ist
		$found = false;
		foreach($this->farben as $item) {
			if($item['farbe'] != $farbe ) {
				$found = true;
				break;
			}
		}
		
		if(($found === false && $farbe != 0) && $this->char->get('aktiviert') != 1) {
			// Farbe wurde nicht gefunden
			logError("Ungültige Farbe gewählt!");
		}
		
		$this->char->unequipped_items[$id]['hexfarbe'] = $farbe;
	}
	
	public function addStartitems($volk, $profession) {
		if(is_int($volk)=== false || is_int($profession)) {
			throw new Exception();	
		}
		
		global $MySQL;
		
		$pakete = $MySQL->fetch_complete(sprintf("SELECT * FROM server.startitem_pakete WHERE volk = %d AND startprofession = %d", $this->char->get('volk'), $this->char->get('profession_1', 'id')));
		$a = true; // TODO
		foreach($pakete as $item) {
			$a = false;
			$this->buyItem((int)$item['herstellungsid'], (int)$item['anzahl'], true, true);
		}
		if ($a == true) {
			throw new Exception;
		}
	}
	
	public function calcGewichtVolumen() {
		$gewicht = 3*3+1*200; // Münzen
		$volumen = 203;	 // Münzen
		
		foreach($this->char->unequipped_items as $item) {
			if(isset($item['her_info']['gewicht'])) {
				assert($item['her_info']['gewicht'] >= 0 && $item['anzahl']>0);
				$gewicht += $item['her_info']['gewicht']*$item['anzahl'];
			}
			if(isset($item['her_info']['volumen'])) {
				assert($item['her_info']['volumen'] >= 0 && $item['anzahl']>0);
				$volumen += $item['her_info']['volumen']*$item['anzahl'];
			}
		}
		foreach($this->char->equipped_items as $item) {
			if($item['her_info']['herstellungsid']===null) {
				// Haar oder Bart
				continue;
			}
			
			if(isset($item['her_info']['gewicht'])) {
				assert($item['her_info']['gewicht'] > 0);
				$gewicht += $item['her_info']['gewicht'];
			}
			if(isset($item['her_info']['volumen'])) {
				assert($item['her_info']['volumen'] > 0);
				$volumen += $item['her_info']['volumen'];
			}
		}
		
		return(array('gewicht' => $gewicht, 'volumen' => $volumen)); 
	}
	
	private function calcValue() {
		$sum = 0;
		
		foreach($this->char->unequipped_items as $item) {
			if(isset($item['her_info']) && ($item['her_info']['herstellungsid'] == 1898 || $item['her_info']['herstellungsid'] == 1899 || $item['her_info']['herstellungsid'] == 1900)) {
				continue;
			}
			
			if(isset($item['her_info']['geldwert']) && isset($item['her_info']['not_sellable'])===false) {
				assert($item['her_info']['geldwert'] >= 0 && $item['anzahl']>0);
				$sum += $item['her_info']['geldwert']*$item['anzahl'];
			}
		}
		foreach($this->char->equipped_items as $item) {
			if($item['her_info']['herstellungsid']===null) {
				// Haar oder Bart
				continue;
			}
			
			if(isset($item['her_info']['geldwert']) && isset($item['her_info']['not_sellable'])===false) {
				assert($item['her_info']['geldwert'] > 0);
				$sum += $item['her_info']['geldwert'];
			}
		}
		return($sum);
	}
	
	public function pimpMyMoney($geld) {
		assert(false);  // Sollte nirgendwo mehr gebraucht werden
		assert(is_int($geld) AND $geld >= 0);
		// name inspired by imp; mehr Gold wird daraus aber hoffentlich nicht
		$gold = floor($geld / 1000); $geld = $geld % 1000;
		$silber = floor(($geld /100));
		$kupfer = ($geld % 100);
		
		return(array('gold' => (int)$gold, 'silber' => (int)$silber, 'kupfer' => (int)$kupfer));
	}
		
	public function unPimpMyMoney($gold, $silber, $kupfer) {
		assert(false);  // Sollte nirgendwo mehr gebraucht werden
		assert(is_int($gold) AND $gold >= 0);
		assert(is_int($silber) AND $silber >= 0);
		assert(is_int($kupfer) AND $kupfer >= 0);

		$geld = $kupfer+100*$silber+1000*$gold;
		
		return((int)$geld);	
	}
	
	private function readTileData() {
		//return;  // Zum neueinlesen aktivieren
		// TODO
		
		// check for availability of tiledata
		if(is_readable($this->tiledatapath)===false) {
			logError("Tiledatapfad nicht gefunden");
		}
		
		global $MySQL, $user;
		
		// tiledata für alle shoppinglist-items auslesen
		$handle = fopen($this->tiledatapath, 'rb');
		
		foreach($this->shoppinglist as $i => $value) {
			// TODO CHECK
			if($value['stackable'] != -1) {
				// für den Gegenstand schon gesetzt => weiter
				 
				continue;	
			}
			/*if($value['itemid']==0 || strlen($value['name'])==0) {
				// evtl. loggen (?)
				
				continue;
			}*/
			
			$itemid = $this->shoppinglist[$i]['itemid'];
			
			fseek($handle, 428032+$itemid*37+(floor($itemid/32)+1)*4, SEEK_SET);
			if(feof($handle)) { 
	            logError("Unexpected feof");
	        }
	        
	        // lese flags
	        if(($value1 = fread($handle, 4)) == FALSE) {
	        	logError("Problem with tiledata");
	        }
	        
	        $v = unpack('l', $value1);
	        $flag = $v[1];
	        
	        // stapelbar
	        if($this->shoppinglist[$i]['faerbbar']== 1 OR (($flag & 0x800) == 0)) {
	        	$this->shoppinglist[$i]['stackable'] = false;
	        } else {
	        	$this->shoppinglist[$i]['stackable'] = true;
	        }
	        
	        // anziehbar
	        //if($this->shoppinglist[$i]['ist_endprodukt'] == 1 AND $this->shoppinglist[$i]['layer'] > 0 AND ($flag & 0x400000)>0) {
	        if($this->shoppinglist[$i]['ist_endprodukt'] == 1 AND $this->getLayer($i) > 0 AND ($flag & 0x400000)>0) {
	        	$this->shoppinglist[$i]['wearable'] = true;
	        } else {
	        	$this->shoppinglist[$i]['wearable'] = false;
	        }
		
			// Ergebnis in der DB speichern. Soll das ganze später mal aktualisiert werden, einfach wieder auf NULL oder -1 (???) setzen. Beim nächsten Skriptupdate werden die Daten eingepflegt
			$MySQL->query(sprintf("UPDATE server.einkaufsliste SET stackable = %d, wearable = %d WHERE herstellungsid = %d", intval($this->shoppinglist[$i]['stackable']), intval($this->shoppinglist[$i]['wearable']), $i));
		}
		fclose($handle);
		return(true);
	
	}
	
	// Überprüft, ob ein Item in der Liste ist und gibt die Stelle zurück
	private function itemAvailable($herstellungsid) {
		$max = count($this->shoppinglist);
		for($i=0;$i<$max;$i++) {
			if($this->shoppinglist[$i]['herstellungsid'] == $herstellungsid) {
				return($i);
			}
		}
		return(false);
	}
	
	private function getLayer($itemid) {
		
		switch($this->shoppinglist[$itemid]['klassenname']) {
			case 'Kriegsgeraet':
				switch('int_1') {
					case 0:
						$layerid = 2; // TODO: mit Arahil abchecken
						break;
					case 1:
						$layerid = 1;
						break;
					case 2:
						$layerid = 2;					
						break;	
				}
			break;
			case 'Lichtquelle':
				$layerid = 2;
			break;
			default:
				//BasisKleidung, BasisEquipbaresItem, usw.
				$layerid = intval($this->shoppinglist[$itemid]['int_0']);
		}

		return($layerid);
	}
	
	public function buyItem($herstellungsid, $anzahl, $nocheck = false, $paket = false) {
		assert(is_int($anzahl) AND $anzahl > 0 AND is_int($herstellungsid));
		if(intval($anzahl) <= 0) logError();
		
		if(isset($this->shoppinglist[$herstellungsid]) === false OR isset($this->shoppinglist[$herstellungsid]['name'])===false)	{
			logError("Dieses Item gibt es nicht!");
		}
		
		// Prüfe, ob es sich nicht um ein Paketitem handelt, das nicht regulär gekauft werden soll
		if($this->shoppinglist[$herstellungsid]['gruppe'] == 99 && $paket === false) {
			throw new Exception();
		}
		
		$itemid = $this->shoppinglist[$herstellungsid]['itemid'];
		if($nocheck == false) {
			$geld = $anzahl * $this->shoppinglist[$herstellungsid]['geldwert'];
			assert($geld > 0);
			
		
			if($this->money - $geld < 0) {
				throw new IException("Du hast nicht genügend Geld!");
			} else {
				$this->money -= $geld;
				
				$this->char->addItem((int)$itemid, (int)$this->shoppinglist[$herstellungsid]['farbe'], (int)$anzahl, $this->shoppinglist[$herstellungsid], false);
			}
		} else {
			// Gold am Ende && Paket ohne Berechnung
			if($paket === true) {
				$myHerinfo = $this->shoppinglist[$herstellungsid]+array('not_sellable' => true);
			} else {
				$myHerinfo = $this->shoppinglist[$herstellungsid];
			}
			$this->char->addItem((int)$itemid, (int)$this->shoppinglist[$herstellungsid]['farbe'], (int)$anzahl, $myHerinfo, false);
		}
		
		
		return(true);
	}
	
	public function sellItem($id) {
		assert(is_int($id));
				
		if(isset($this->char->unequipped_items[$id])===false) return(false);
		
		if(isset($this->char->unequipped_items[$id]['her_info']['not_sellable'])) {
			throw new IException('Dies gehört zur Startausrüstung und kann nicht verkauft werden.');
		}
		
		$this->money += $this->char->unequipped_items[$id]['anzahl']*$this->char->unequipped_items[$id]['her_info']['geldwert'];
		
		unset($this->char->unequipped_items[$id]);
				
		return(true);
	}
	
	public function equip($id, $herstellungsid = false) {
		if(isset($this->char->unequipped_items[$id])===false) return(false);

		// prüfen, ob der richtige Gegenstand gemeint ist
		if($herstellungsid != false && $herstellungsid != $this->char->unequipped_items[$id]['her_info']['herstellungsid']) {
			// evtl. wurde zu schnell geklickt
			return(false);
		}
		
		// prüfen, ob Gegenstand anziehbar ist
		if($this->char->unequipped_items[$id]['her_info']['wearable']===false) {
			//logError("Item $id () is not wearable");
			//TODO!!!
			global $MySQL, $user;

			$backtrace = debug_backtrace();						
			$dump.= sprintf("Wearable immer noch aktuell: %s ||| %s", Print_R($backtrace, true), Print_R($GLOBALS, true));
		
			$sql = sprintf("INSERT INTO logdb.me_errors SET typ = 69, accountID = %d, zeit = NOW(), meldung = '%s', service = 'homepage'", $user->ID, me_escape($dump));
			
			$MySQL->query($sql);
		}
		// Hole Layer
		$layerid = $this->getLayer($this->char->unequipped_items[$id]['her_info']['herstellungsid']);
		
		
		// checke, ob Layer schon belegt ist=>Ersetzen und zurück in Items
		if(is_null($this->char->equipped_items[$layerid])===false) {
			// Layer schon belegt, Item unequippen
			$this->unequip($layerid);
		}
		
		// Checke Einhändig/Zweihändig; wir sind hier etwas unsauber und erlauben keine Fackel auf 2händig und 1 Schwert
		if($layerid == 1 &&  is_null($this->char->equipped_items[2])===false) {
			$this->unequip(2);
		}
		if($layerid == 2 &&  is_null($this->char->equipped_items[1])===false) {
			$this->unequip(1);
		} 
		
		if($this->char->unequipped_items[$id]['anzahl'] > 1) {
			// nur eines equippen; Anzahl um eines reduzieren
			$this->char->equipped_items[$layerid]=$this->char->equipped_items[$layerid] = $this->char->unequipped_items[$id]; $this->char->equipped_items[$layerid]['anzahl'] = 1;
			$this->char->unequipped_items[$id]['anzahl']--;
		} else {
			$this->char->equipped_items[$layerid]=$this->char->equipped_items[$layerid] = $this->char->unequipped_items[$id]; $this->char->equipped_items[$layerid]['anzahl'] = 1;
			unset($this->char->unequipped_items[$id]);
		}
				
		return(true);
	}
	
	public function unequip($layerid) {
		if(is_int($layerid) === false || $layerid == 11 || $layerid == 16 || $layerid <= 0) {
			LogError("Ung&uuml;ltiger Layer!");
		}
	
		if(is_null($this->char->equipped_items[$layerid])===true || isset($this->char->equipped_items[$layerid]) === false) {
			throw new IException("Du trägst das nicht mehr!");
		}
		$found = false;
		if($this->char->equipped_items[$layerid]['her_info']['stackable']===true AND count($this->char->unequipped_items)>0) {
			
			foreach($this->char->unequipped_items as $key => $value) {
					if($value['her_info']['herstellungsid']!=$this->char->equipped_items[$layerid]['her_info']['herstellungsid']) {
						continue;	
					}
					if(isset($value['her_info']['not_sellable']) === true) {
						continue;	
					}
					
					// gefunden
					$found = true;
					$this->char->unequipped_items[$key]['anzahl']++;
					break;
				}
		}
		if($found === false) {
			$this->char->unequipped_items[] = $this->char->equipped_items[$layerid];
		}
		$this->char->equipped_items[$layerid] = null;
		
		
		
		return(true);
	}
	
	
	public function finishShopping() {
		if($this->money > $this->max_money) {
			logError("Plötzlich mehr Geld geworden!");
		}
		// TODO check check functions
		
		// Geldwert der gekauften Items + Restgeld != max_money
		if($this->money+$this->calcValue() != $this->max_money) {
			logError("Fehler beim Gold!");
		}
		
		// verbleibendes Geld speichern
		//$this->buyItem(1898, $gsk['gold']);
		// calcGewichtVolumen() auch ggf. ändern
		$this->buyItem(1899, 3, true);
		$this->buyItem(1900, 200, true);
		return(true);
	}
	
	public function getShoppinglist() {
		$shoppinglist = array();
		foreach($this->shoppinglist as $key => $item) {
			if($key == 1898 || $key == 1899 || $key == 1900) {
				// Gold kann man nicht händisch erschoppen; wird zugeteilt
				continue;
			}
			// TODO - better	
			$shoppinglist[$key] = $this->shoppinglist[$key];
		}	
		return($shoppinglist);
	}
}
