/**	 NOTE APP */
var notes = (function(){
	/** 
		app initialization
		
	*/
	var data = [];
			
	var noteObj = function(obj){
		this.title 	= obj.title;
		this.tags 	= obj.tags;
		this.content = obj.content;
		/**  view parameters */
		//	if note is opened
		this.opened = obj.opened;
		//	object with css information such: width, height, zindex etc.
		this.css = obj.css;
	};
	noteObj.prototype = {
		 getTitle: function(){
			/**
				funkcja zwraca tytuł notatki
				
			*/
			return this.title;
		}
		,setTitle: function(title) {
			/**
				funkcja zmieniajaca tytuł notatki
				
				@param 		title		string z tytułem notatki
				
				@return		this		obiekt pojedynczej notatki
				
			*/
			title = title.toString();
			this.title = title;
			return this;
		}
		,getContent: function(){
			/**
				funkcja zwraca content notatki - string
				
			*/
			return this.content;
		}
		,setContent: function(txt){
			/**
				funkcja zmienia treść notatki
				
				@param		txt			string z nową treścią dla notatki
				
				@return		this		obiekt pojedynczej notatki
				
			*/
			this.content = txt;
			return this;
		}
		,getTags: function(){
			/**
				funkcja zwraca tablicę z tagami. 
				
				@return		array		tablica indeksowana numerycznie, jej elementami są wartości typu string
				
			*/
			return this.tags;
		}
		,setTags: function(arr){
			/**
				funkcja nadpisuje całą tablicę z tagami
				
				@param		arr		tablica indeksowana numerycznie z listą tagów (stringi)
				
				@return		this	pojedynczy obiekt notatki
				
				Uwaga!
				zostaną nadpisane wszystkie ustawione wcześniej tagi!
				
				@todo		sprawdzać tablicę czy jest tablicą ;]
				
			*/
			this.tags = arr;
			return this;
		}
		,addTag: function(tag) {
			/**
				funkcja dodaje tag do notatki
				
				@param		tag		string - nazwa tagu, którym ma zostać ozanczona notatka
				
				@return		this	pojedynczy obiekt notatki jak tag został poprawnie dodany
							false	jesli taki tag juz istnieje i nie został dodany do bazy
				
				@description		
				
				funkcja sprawdza czy taki tag jest już przypisany do danej notatki, jeśli tak, to nic się nie zmienia, a funkcja zwraca obiet notatki. W przeciwnym wypadku nowy tag jest dodawany na końcu tablicy, a następnie zwracany jest obiekt notatki.
				
			*/
			var i=0, ile=this.tags.length;
			for(;i<ile;i++){
				if ( this.tags[i] == tag ) {
					log('[info] noteObj.setTag |> tag already exist');
					return false;
				}
			}
			this.tags.push(tag);
			return this;
		}
		,removeTag: function(tag) {
			/**
				funkcja usuwa istniejący tag
				
				@param		tag			* string - nazwa tagu, który ma zostać usunięty z listy tagów przypisanych do notatki
										* number - indeks tagu, który ma zostać usunięty z listy tagów przypisanych do notatki
				
				@return 	this		pojedynczy element notatki
				
				@todo	obsługa number
				
			*/
			var i=0, ile=this.tags.length;
			for(;i<ile;i++){
				if ( this.tags[i] == tag ) {
					this.tags.remove(i);
					return this;
				}
			}
			log('[info] noteObj.removeTag |> tag not found');
			return this;
		}
		,eachTag: function(fn) {
			/**
				funkcja wywołująca przekazaną funkcję na każdym pojedycznym tagu przypisanym do notatki
				
				@param		fn		referencja do funkcji do wywołania
				
				@return		this 	pojedynczy obiekt notatki
				
				@description
				
				Kontekstem wykonania funkcji fn jest pojedynczy tag (string). Jako kolejne parametry przekazane do funkcji są: obiekt pojedynczej notatki oraz indeks danego tagu.
				
			*/
			if ( typeof fn !== 'function' ) {
				log('[error] noteObj.each |> fn is not a function');
			}
			var	 i = 0
				,d = this.tags
				,ile = d.length
			;
			for(;i<ile;i++) {
				fn.call( d[i], this, i );
			}
			return this;
		}
	};

	/** public notes object */
	return {
		 addNote: function(note) {
			/**
				metoda dodaje nową notatkę, przekazaną jako parametr

				@param	note	obiekt z notatką.
				
			*/
			//	przygotowanie obiektu z notatką 
			// sprawdzenie przekazanych parametrów
			if ( !note ) {
				log('[error] notes.addNote |> param not found');
				return false;
			}
			var n = $.extend(true, {}, {
					 title: '*untitled*'
					,tags: []
					,content: 'empty'
					,opened: false
					,css: { } 
				} , note);
			
			data.push(new noteObj(n));
			return notes;
		}
		,getNote: function(index) {
			/**
				metoda zwraca notatkę o określonym indeksie, 
				w przeciwnym wypadku zwraca false.
				
				@param		index	
				
					indeks (integer) notatki, do której chcemy się dostać.
				
				@return 	noteObj/false
				
			*/
			if ( index < data.length && typeof data[index] != 'undefined' ) {
				return data[index];
			}
			return false;			
		}
		,getLast: function(){
			return this.getNote( data.length-1 );
		}
		,removeNote: function(index) {
			/**
				metoda usuwa wybraną notatkę
				
				@param		index			
				
					indeks (integer) notatki, którą chcemy usunąć
				
				@return		notes/obj		
				
					zwracany jest obiekt notes, jeśli notatka została poprawnie
					usunięta. w przeciwnym wypadku jest zwracana wartość false.
				
			*/
			if ( index < data.length && typeof data[index] != 'undefined' ) {
				nots.data.remove(index);
				return notes;
			}
			return false;			
		}
		,each: function(fn) {
			/**
				metoda iteruje po wszystkich notatkach i wywołuje funkcję, ustawiając this na obiekt danej notatki
				
				@param		fn		funkcja do wywołania na każdej notatce
				
				@return		notes
				
			*/
			if ( typeof fn != 'function' ) {
				log('[error] notes.each |> fn is not a function');
			}
			var	 i = 0
				,d = data
				,ile = d.length
			;
			for(;i<ile;i++) {
				fn.call( d[i] );
			}
			return notes;
		}
		,find: function(search, fn) {
			/**
				metoda znajdująca notatki o podanych kryteriach
				
				@param		search					obiekt z informacją o poszukiwanych informacjach
													/ możliwe pola do poszukiwania: 
													title, tag, content /													
													LUB string z szukaną frazą: wtedy wyszukiwanie następuje wszędzie.
													
				@param		fn						[opcjonalnie]
													funkcja, która zostanie wywołana na każdym obiekcie znalezionej notatki.
													kontekst wywołania funkcji = pojedynczy obiekt znalezionej notatki.
													parametry przekazane do funkcji: wyszukiwana fraza
				
				@return		array of noteObj 		tablica z obiektami notatek
				
			*/
			if ( !search ) {
				log('[error] notes.find |> search param is required');
			}
			if ( typeof search === 'string' ) {
				search = {
					 tag: search
					,title: search
					,content: search
				};
			}
			//	przygotowanie zmiennych ;]
			var a = [];			//	tablica z listą znalezionych notatek
			//	przechodzimy po każdej notatce
			notes.each(function(){
				var _note = this;
				//	sprawdzamy czy szukamy czegoś w title
				if ( typeof search.title !== 'undefined' ) {
					if ( _note.getTitle().indexOf(search.title) !== -1 ) {
						a.push(_note);
						if ( typeof fn === 'function' ) {
							fn.call( _note, search.title );
						}
						return true;
					};
				}
				//	czy szukamy czegoś w content
				if ( typeof search.content !== 'undefined' ) {
					if ( _note.getContent().indexOf(search.content) !== -1 ) {
						a.push(_note);
						if ( typeof fn === 'function' ) {
							fn.call( _note, search.content );
						}
						return true;
					};
				}
				//	 czy szukamy w tagach ?
				if ( typeof search.tag !== 'undefined' ) {
					_note.eachTag(function(){
						if ( this+'' === search.tag ) {
							a.push(_note);
							if ( typeof fn === 'function' ) {
							fn.call( _note, search.tag );
						}
							return true;
						}
					});
				}
			});
			//	zwracamy tablicę wyników z listą notatek.
			return a;
		}
	
		,save: function(){
			var s = [];
			notes.each(function(item){
				var n = {};
				n.content = this.content;
				n.title = this.title;
				n.tags = this.tags;
				n.opened = this.opened;
				n.css = this.css;
				
				if ( typeof this.css !== 'undefined' ) {
					n.css = this.css;
				}
				if ( typeof this.opened !== 'undefined' ) {
					n.opened = this.opened;
				}
				s.push(n);
			});
			$.jStorage.set('notes', s);
			log('saved : '+$.jStorage.storageSize()+' bytes');
			log(s);
			return s;
		}
		,load: function(s){
			if ( !s ) {
				s = $.jStorage.get('notes');
			}
			if ( !s ) {
				return false;
			}
			var
				i=0, ile=s.length || 0
			;
			data = [];
			for(;i<ile;i++){
				notes.addNote( s[i] );
			}
		}
		,toString: function(){
			log (' *** notes *** ');
			this.each(function(){
				log(this);
			});
			log (' /// notes  ');
		}
	}
	
})();
