define(
	[
		'table',
		'input',
		'utils'
	],
	function (table, input, utils) {
		"use strict";

		table.extend('input', (function() {
			//TODO add showCorrect/showIncorrect
			//TODO add removeIncorrect

			function TableInput() {
				this.table = null;
				this.events = ['change', 'retry', 'feedback'];
				this.type = '';
				this.rows = {
					group: true
				};
				this.cols = {
					group: false
				};
				this.answer = [];
				this.attempt = [];
				this.textOptions = { //input[type=text] and textarea
					showAnswerInPlace: true, //answer is shown within input if true, below if false (allows html to be used)
					hideAttemptOnReveal: true //hides input if false only when showAnswerInPlace = false
				};
				this.options = {
				    checkbox: {
				        markCheckedOnly: true
                    },
				    text: { //input[type=text] and textarea
                        showAnswerInPlace: true, //answer is shown within input if true, below if false (allows html to be used)
                        hideAttemptOnReveal: true //hides input if false only when showAnswerInPlace = false
                    }
                };
				this.dom = [];
			}

			TableInput.prototype.init = function() {
				this.initInputs();

				if (this.answer.length > 0) {
					this.setAnswer();
				}

				this.initAnswer();
			};

			TableInput.prototype.removeInputs = function() {
				if (this.dom.length > this.table.rows.num) {
					this.dom.pop();
				}

				this.dom.forEach(function(row) {
					if (row.length > this.table.cols.num) {
						row.pop();
					}
				}.bind(this));
			};

			TableInput.prototype.updateInputs = function() {
				this.removeInputs();
				this.initInputs();
			};

			TableInput.prototype.initInputs = function() {
				this.table.content.forEach(function(data, row) {
					this.dom[row] = this.dom[row] || [];

					data.forEach(function(data, col) {
						if (typeof this.dom[row][col] !== 'undefined') {
							return;
						}

						if (data === null) {
							return;
						}

						if (!data.input) {
							if (!input.isInput(this.type) || this.type == 'select' || typeof data.html !== 'undefined') {
								this.dom[row].push(null);
								return;
							}

							data.input = {
								type: this.type
							};
						}

						this.createInputs(data, row, col);
					}.bind(this));
				}.bind(this));
			};

			TableInput.prototype.createInputs = function(data, row, col) {
				var cell = this.table.cols.dom[col][row];
				var grouped = (this.rows.group || this.cols.group);

				//ensure array
				data.input = Array.isArray(data.input) ? data.input : [data.input];

				data.input.forEach(function(data) {
					data.type = data.type || this.type;
					utils.addClass(cell, 'cell-' + data.type);

					switch (data.type) {
						case 'checkbox':
							data.attempt = data.checked ? 1 : 0;
							break;
						case 'radio':
                            data.attempt = data.checked ? 1 : 0;

                            if (grouped) {
								//where single radio to a cell, group as rows unless set to cols
								data.name = 'radio-group' + (this.cols.group ? col : row);
							}
							break;
						case 'text':
						case 'textarea':
							data.attempt = '';
							break;
						case 'select':
							if (typeof data.options == 'number') {
								data.shuffle = !!this.options[data.options].shuffle;
								data.options = this.options[data.options].data;
							}
					}

					if (!data.label && input.isInput(data.type)) {
						this.setLabel(cell, data, row, col);
					}
				}.bind(this));

				cell.appendChild(input.create(data.input));

				this.dom[row][col] = [];

				data.input.forEach(function(data) {
					this.dom[row][col].push(data.el);
					delete data.el;
					delete data.label;
				}.bind(this));
			};

            /**
             * Set answer data in this.table.content if answer provided separately (this.answer)
             */
            TableInput.prototype.setAnswer = function() {
                var array;

                this.table.content.forEach(function(row) {
                    if (row.some(function(cell) { return cell && cell.input; })) {
                        array = this.answer.shift();

                        row.forEach(function(cell) {
                            if (cell && cell.input) {
                                cell.input.forEach(function(input) {
                                    input.answer = array.shift();
                                });
                            }
                        });
                    }
                }.bind(this));
            };

            /**
             * Any type specific initialisation of answer
             */
            TableInput.prototype.initAnswer = function() {
                this.table.iterateContent(function(row, cell) {
                    if (cell && cell.input) {
                        cell.input.forEach(function(input) {
                            switch (input.type) {
                                case 'checkbox':
                                case 'radio':
                                    if (this.options.checkbox.markCheckedOnly && input.answer === 0) {
                                        input.answer = null;
                                    }
                            }
                        }.bind(this));
                    }
                }.bind(this));
            };

            TableInput.prototype.setLabel = function(cell, data, row, col) {
				var header = (this.table.cols.headers.indexOf(row) > -1 || this.table.rows.headers.indexOf(col) > -1);
				var str = '';

				//where cell isn't a header, use the nearest col/row header if one/both exist
				if (!header) {
					if (this.table.cols.headers.length > 0 || this.table.rows.headers.length > 0) {
						if (this.table.cols.headers.length > 0) {
							data.label = [];
							data.label.push(this.table.cols.dom[col][utils.nextLowest(this.table.cols.headers, row)]);
						}
						if (this.table.rows.headers.length > 0) {
							data.label = data.label || [];
							data.label.push(this.table.cols.dom[utils.nextLowest(this.table.rows.headers, col)][row]);
						}
						return;
					}
				}

				//last resort - indicate if head and/or give cell position
				str += (header ? 'Table head: ' : '');
				str += 'Row ' + (row + 1) + ', column ' + (col + 1);

				data.label = document.createElement('label');
				data.label.textContent = str;
				data.label.className = 'screen-reader';
				cell.appendChild(data.label);
			};

			TableInput.prototype.change = function(e) {
				var target = e.target;
				var row, col, data, index;

				while (target.tagName.toLowerCase() != 'th' && target.tagName.toLowerCase() != 'td') {
					target = target.parentNode;
				}

				//a bit of dom querying here but a small price to pay not to have a handler per input
				row = parseInt(target.getAttribute('data-row'), 10);
				col = parseInt(target.getAttribute('data-col'), 10);
				data = this.table.content[row][col];
				//index incorrect where radios grouped across cells so make sure we get the right one...
				index = (data.input.length > 1) ? parseInt(e.target.getAttribute('data-input-group-index'), 10) : 0;
				data = data.input[index];

				switch (data.type) {
					case 'checkbox':
						data.attempt = e.target.checked ? 1 : 0;
						break;
                    case 'radio':
                        data.attempt = e.target.checked ? 1 : 0;
                        this.updateRadioGroup(row, col, index);
                        break;
					case 'text':
					case 'textarea':
						data.attempt = e.target.value;
						break;
					case 'select':
						data.attempt = parseInt(e.target.value, 10);
				}

				console.log(data.attempt);
			};

			/**
			 * update data for radio group
			 * @param {number} row - row index
			 * @param {number} col - col index
			 * @param {number} index - input index
			 */
			TableInput.prototype.updateRadioGroup = function(row, col, index) {
				if (this.cols.group) {
					this.table.iterateContent(function(data, cell, i, j) {
						if (i != row && j == col && cell.input) {
							cell.input.forEach(function(input) {
								input.attempt = 0;
							});
						}
					});
					return;
				}

				if (this.rows.group) {
					this.table.content[row].forEach(function(cell, i) {
						if (i != col && cell.input) {
							cell.input.forEach(function(input) {
								input.attempt = 0;
							});
						}
					});
					return;
				}

				this.dom[row][col].forEach(function(input, i) {
					if (i != index) {
						input.attempt = 0;
					}
				});
			};

			/**
			 * Set this.attempt/answer from this.table.content, ready for comparison
			 * @param {boolean} attempt - attempt if true, answer otherwise
			 */
			TableInput.prototype.getAnswer = function(attempt) {
				var answer = attempt ? 'attempt' : 'answer';

				//pull answer/attempt out from content
				this.table.content.forEach(function(row, i) {
					this[answer][i] = this[answer][i] || [];

					row.forEach(function(cell, j) {
						if (cell && cell.input) {
							cell.input.forEach(function(input, k) {
								this[answer][i][j] = this[answer][i][j] || [];
								this[answer][i][j][k] = input[answer];
							}.bind(this));
						}
						else {
							this[answer][i][j] = null;
						}
					}.bind(this));
				}.bind(this));

				if (!attempt) {
					this.getAnswer(true);
				}
			};

			/**
			 * Compare this.attempt to this.answer. Mutates this.attempt with true/false values.
			 */
			TableInput.prototype.checkAnswer = function() {
                this.disableInputs();
				this.getAnswer(false);

                console.log('Answer: ', this.answer);
				console.log('Attempt: ', this.attempt);

				this.table.trigger('feedback', {
					attempt: this.attempt,
					answer: this.answer
				});
			};

			//TODO refactor...
			/**
			 * Toggle attempt/answer
			 * @param {boolean} attempt - attempt if true, answer otherwise
			 */
			TableInput.prototype.toggleAnswer = function(attempt) {
				var answer = attempt ? 'attempt' : 'answer';
				var inputs, index;

				if (attempt) {
				    this.enableInputs();
                }
                else {
				    this.disableInputs();
                }

				this.table.content.forEach(function(row, i) {
					row.forEach(function(data, j) {
						if (data === null || typeof data.input === 'undefined') {
							return;
						}

						inputs = this.dom[i][j];
						data = Array.isArray(data.input) ? data.input : [data.input];

						data.forEach(function(data, k) {
							switch (data.type) {
								case 'radio':
								case 'checkbox':
									inputs[k].checked = !!data[answer];
									break;
								case 'text':
								case 'textarea':
									if (this.textOptions.showAnswerInPlace) {
										inputs[k].value = data[answer] || '';
									}
									else {
										if (attempt) {
											inputs[k].value = data[answer] || '';
										}
										else if (inputs[k].nextSibling.textContent === '') {
											inputs[k].nextSibling.insertAdjacentHTML('beforeend', (data[answer] || ''));
										}

										if (this.textOptions.hideAttemptOnReveal) {
											inputs[k].style.display = attempt ? 'block' : 'none';
										}
										else {
											inputs[k].style.marginBottom = attempt ? '0' : '20px';
										}

										inputs[k].nextSibling.style.display = attempt ? 'none' : 'block';
									}

									//TODO find less hacky way to get true scroll height...
									inputs[k].style.height = '0';
									inputs[k].style.height = attempt ? '100%' : inputs[k].scrollHeight + 'px';
									this.table.cols.dom[j][i].style.backgroundColor = attempt ? '' : '#ffffff';
									break;
								case 'select':
									index = 0;

									//TODO better way??
									if (typeof data[answer] == 'number') {
										utils.toArray(inputs[k].children).some(function(el, i) {
											if (el.value == data[answer]) {
												index = i;
												return true;
											}
										});
									}

									inputs[k].selectedIndex = index;
							}
						}.bind(this));
					}.bind(this));
				}.bind(this));

				utils.resize_iframe();
			};

			/**
			 *
			 * @param {string} type - col/row
			 * @param {number} index - col/row index, otherwise last col/row
			 * @returns {boolean}
			 */
			TableInput.prototype.hasContent = function(type, index) {
				if (type == 'row') {
					index = (typeof index == 'number') ? index : this.table.rows.num - 1;

					return this.table.content[index].some(function(data) {
						return data.attempt;
					});
				}

				if (type == 'col') {
					index = (typeof index == 'number') ? index : this.table.cols.num - 1;

					return this.table.content.some(function(row) {
						return row.some(function(cell, i) {
							return i == index && row[index].attempt;
						}.bind(this));
					}.bind(this));
				}
			};
			/////////////////////////


            TableInput.prototype.enableInputs = function(inputs) {
                inputs = inputs || this.dom;

                inputs.forEach(function(input) {
                    if (Array.isArray(input)) {
                        this.enableInputs(input);
                    }
                    else {
                        if (input) {
                            input.removeAttribute('disabled');
                        }
                    }
                }.bind(this));
            };

            TableInput.prototype.disableInputs = function(inputs) {
                inputs = inputs || this.dom;

                inputs.forEach(function(input) {
                    if (Array.isArray(input)) {
                        this.disableInputs(input);
                    }
                    else {
                        if (input) {
                            input.disabled = true;
                        }
                    }
                }.bind(this));
            };

            /**
			 * Reset input data and show.
			 */
			TableInput.prototype.reset = function() {
				this.table.iterateContent(function(row, cell) {
					if (cell === null) {
						return;
					}

					if (cell.input && cell.input.length > 0) {
						cell.input.forEach(function(data) {
							data.attempt = null;
						}.bind(this));
					}
				});

				this.toggleAnswer(true);
                this.enableInputs();
            };

            TableInput.prototype.retry = function() {
                console.log('retry');
                this.enableInputs();
            };

            /**
			 * Handles all module events
			 * @param {event} e
			 * @param {string} e.type
			 * @param {Element} e.target
			 */
			TableInput.prototype.handleEvent = function(e) {
				switch(e.type) {
					case 'data-loaded':
						console.log('update inputs');
						this.updateInputs();
						this.toggleAnswer(true);
						break;
					case 'check':
                        this.checkAnswer();
						break;
					case 'change':
						this.change(e);
						break;
					case 'reveal':
						this.toggleAnswer(e.target.getAttribute('aria-pressed') != 'true');
						break;
					case 'reset':
						this.reset();
						break;
					case 'retry':
						this.retry();
						break;
					case 'extend':
						this.initInputs();
						break;
					case 'reduce':
						this.removeInputs();
				}
			};

			return {
				construct: function() {
					return new TableInput();
				}
			};
		})());
	}
);