import util from 'util';

export default function *() {

	// Get current store from state or else set a default if it exists
	var store = this.getState('Blockly', {
		workspace: null,
		toolbox: null,
		registered: {},
		pinTable: { 
			D2: 2,
			D3: 3,
			D4: 4,
			D5: 5,
			D6: 6,
			D7: 7,
			D8: 8,
			A0: 'A0',
			A1: 'A1',
			A2: 'A2',
			A3: 'A3',
			A4: 'A4',
			I2C1: 1,
			I2C2: 2,
			I2C3: 3,
			I2C4: 4
		},
		definitions: {}
	});

	var updateAvailableModule = function(moduleId, pin) {

		var newType = 'genuino_' + moduleId + '_' + pin;

		// Unregister the old module with specific pin
		for (var type in store.registered) {
			var module = store.registered[type];

			if (module.pin == pin) {
				delete store.registered[type];
				delete window.Blockly.Blocks[module.type];
				delete window.Blockly.Arduino[module.type];

				// Reduce reference
				for (var index in module.definitions) {
					var def = module.definitions[index];

					// Need to substitue pin number
					if (defs.indexOf('%s') != -1) {
						defs = util.format(defs, store.pinTable[pin]);
					}

					store.definitions[def]--;

					if (!store.definitions[def]) {
						delete store.definitions[def];
						delete window.Blockly.Arduino.definitions_['var_' + defs];
					}
				}
				// TODO: remove the old block which is on the workspace then refresh
				break;
			}
		}

		if (moduleId == null) {
			return;
		}

		// Getting module information
		var found = false;
		var workboard = this.getState('WorkBoard')
		var modules = workboard.modules.sensors;
		for (var index in modules) {
			var m = modules[index];

			if (m.sku == moduleId) {
				found = true;
				break;
			}
		}

		if (!found)
			return;


		// Create new blocks for blockly
		if (m.hasOwnProperty('methods')) {
			for (var index in m.methods) {
				var method = m.methods[index];
				var moduleType = newType + '_' + method.name;

				// Register the new available module on pin
				var module = store.registered[moduleType] = {
					id: moduleId,
					type: moduleType,
					pin: pin,
					info: m,
					values: method.values || {},
					definitions: []
				};

				// Block definition
				var block = {
					init: function() {
						method.defBlock.args0[1].options = [[ String(pin), String(store.pinTable[pin]) ]];

						this.jsonInit(method.defBlock);
					}
				};

				if (method.isVariable) {
					block.getVars = function() {
						return [ this.getFieldValue('VAR') ];
					};

					block.renameVar = function(oldName, newName) {
						if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) {
							this.setFieldValue(newName, 'VAR');
						}
					};

					//block.contextMenuType_ = 'variables_set';
					block.customContextMenu = function(options) {
						return;
						/*
						var option = {enabled: true};
						var name = this.getFieldValue('VAR');
						option.text = this.contextMenuMsg_.replace('%1', name);
						var xmlField = goog.dom.createDom('field', null, name);
						xmlField.setAttribute('name', 'VAR');
						var xmlBlock = goog.dom.createDom('block', null, xmlField);
						xmlBlock.setAttribute('type', this.contextMenuType_);
						option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock);
						options.push(option);
						*/
					};
				}

				window.Blockly.Blocks[moduleType] = block;

				// Code generator
				window.Blockly.Arduino[moduleType] = function() {
					var pinNum = store.pinTable[pin];

					if (m.hasOwnProperty('init')) {
						window.Blockly.Arduino.setups_['setup_' + newType + '_' + index] = m.init(pinNum) + '\n';
					}

					// Include header files
					if (m.hasOwnProperty('headers')) {
						for (var index in m.headers) {
							var header = m.headers[index];
							window.Blockly.Arduino.definitions_['define_' + header] = '#include <' + header + '>\n';
						}
					}

					// Definitions
					if (m.hasOwnProperty('definitions')) {
						for (var index in m.definitions) {
							var defs = m.definitions[index];

							// Need to substitue pin number
							if (defs.indexOf('%s') != -1) {
								defs = util.format(defs, pinNum);
							}
							module.definitions.push(defs);

							//window.Blockly.Arduino.definitions_['var_' + newType + '_' + index] = util.format(defs, store.pinTable[pin]) + '\n';

							// Reference
							if (!store.definitions[defs]) {
								window.Blockly.Arduino.definitions_['var_' + defs] = defs + '\n';
								store.definitions[defs] = 0;
							} else {
								store.definitions[defs]++;
							}
						}
					}

					return method.codeGen.bind(this)(window.Blockly);
				};
			}
		}

		// Support standard input method
		if (m.input) {
			var moduleType = newType + '_input';

			// Register the new available module on pin
			var module = store.registered[moduleType] = {
				id: moduleId,
				type: moduleType,
				pin: pin,
				info: m
			};

			window.Blockly.Blocks[module.type] = {
				init: function() {
					this.setColour(190);

					// Read-only
					this.appendDummyInput()
						.appendField('读取' + module.info.name)
						.appendField(new Blockly.FieldImage(module.info.image, 64, 64))
						.appendField('PIN#')
						.appendField(new Blockly.FieldDropdown([[ String(pin), String(store.pinTable[pin]) ]]), 'PIN');

					this.setOutput(true, 'Number');
				}
			};

			window.Blockly.Arduino[module.type] = function() {
				var pin = this.getFieldValue('PIN');

				if (m.hasOwnProperty('init')) {
					window.Blockly.Arduino.setups_['setup_' + newType + '_' + index] = m.init(pin) + '\n';
				} else {
					window.Blockly.Arduino.setups_['setup_' + newType + '_' + index] = 'pinMode(' + pin + ', INPUT);\n';
				}

				var code = '';
				if (m.group.indexOf('digital') > -1 && m.group.indexOf('pwm') == -1) {
					code = 'digitalRead(' + pin + ')';
				} else {
					if (workboard.pinDef.hasOwnProperty('D' + pin)) {
						if (workboard.pinDef['D' + pin].indexOf('pwm') > -1) {
							code = 'analogRead(' + pin + ')';
						} else {
							code = 'digitalRead(' + pin + ')';
						}
					} else {
						code = 'analogRead(' + pin + ')';
					}
				}

				return [ code, window.Blockly.Arduino.ORDER_ATOMIC ];
			};
		}

		// Support standard output method
		if (m.output) {
			var moduleType = newType + '_output';

			// Register the new available module on pin
			var module = store.registered[moduleType] = {
				id: moduleId,
				type: moduleType,
				pin: pin,
				info: m
			};

			window.Blockly.Blocks[module.type] = {
				init: function() {
					this.setColour(190);

					var input = this.appendDummyInput()
							.appendField('设定' + module.info.name)
							.appendField(new Blockly.FieldImage(module.info.image, 64, 64))
							.appendField('PIN#')
							.appendField(new Blockly.FieldDropdown([[ String(pin), String(store.pinTable[pin]) ]]), 'PIN');

					if (m.group.indexOf('digital') > -1 && m.group.indexOf('pwm') == -1) {
						input.appendField('stat')
							.appendField(new Blockly.FieldDropdown([['高电平', '1'], ['低电平', '0']]), 'STAT');
					} else if (workboard.pinDef[pin].indexOf('pwm') > -1) {
						input.appendField('stat')
							.appendField(new Blockly.FieldTextInput(0), 'STAT');
					}

					this.setPreviousStatement(true, null);
					this.setNextStatement(true, null);
				}
			};

			window.Blockly.Arduino[module.type] = function() {
				var pin = this.getFieldValue('PIN');
				var stat = this.getFieldValue('STAT');

				if (m.hasOwnProperty('init')) {
					window.Blockly.Arduino.setups_['setup_' + newType + '_' + index] = m.init(pin) + '\n';
				} else {
					window.Blockly.Arduino.setups_['setup_' + newType + '_' + index] = 'pinMode(' + pin + ', OUTPUT);\n';
				}

				var code = '';
				if (m.group.indexOf('digital') > -1 && m.group.indexOf('pwm') == -1) {
					code = 'digitalWrite(' + pin + ', ' + stat + ');\n';
				} else {
					if (workboard.pinDef.hasOwnProperty('D' + pin)) {
						if (workboard.pinDef['D' + pin].indexOf('pwm') > -1) {
							code = 'analogWrite(' + pin + ', ' + stat + ');\n';
						} else {
							code = 'digitalWrite(' + pin + ', ' + stat + ');\n';
						}
					} else {
						code = 'analogWrite(' + pin + ', ' + stat + ');\n';
					}
				}

				return code;
			};
		}
	}.bind(this);

	this.on('store.Blockly.initialize', function *(workspace, toolbox) {

		var store = this.getState('Blockly');

		store.workspace = workspace;
		store.toolbox = toolbox;

		workspace.addChangeListener(function() {
			var xml = window.Blockly.Xml.workspaceToDom(workspace);
			var xml_text = window.Blockly.Xml.domToPrettyText(xml);

			console.log(xml_text);
		});

		// init block
		window.Blockly.Blocks['entry'] = {
			init: function() {
				this.jsonInit({
					'message0': '初始化工作',
					'args0': [],
					'colour': 330,
				});

				this.appendStatementInput('DO')
					.appendField('执行');
			}
		};

		window.Blockly.Arduino['entry'] = function() {

			var branch = Blockly.Arduino.statementToCode(this, 'DO');
			if (Blockly.Arduino.INFINITE_LOOP_TRAP) {
				 branch = Blockly.Arduino.INFINITE_LOOP_TRAP.replace(/%1/g,
					'\'' + this.id + '\'') + branch;
			}

			Blockly.Arduino.entries_['entry'] = branch;

			return null;
		};


		// High and Low
		window.Blockly.Blocks['high_low'] = {
			init: function() {
				this.jsonInit({
					'message0': '%1',
					'args0': [
						{ 'type': 'field_dropdown', 'name': 'Mode', 'options': [ [ '高电平', '1' ], [ '低电平', '0' ] ] }
					],
					'colour': 190,
					'output': true
				});
			}
		};

		window.Blockly.Arduino['high_low'] = function() {
			var mode = this.getFieldValue('Mode');

			return [ mode, window.Blockly.Arduino.ORDER_ATOMIC ];
		};

		// Customized blocks
		window.Blockly.Blocks['light_switch'] = {
			init: function() {
				this.jsonInit({
					'message0': '%1',
					'args0': [
						{ 'type': 'field_dropdown', 'name': 'Mode', 'options': [ [ '亮', '1' ], [ '不亮', '0' ] ] }
					],
					'colour': 190,
					'output': true
				});
			}
		};

		window.Blockly.Arduino['light_switch'] = function() {
			var mode = this.getFieldValue('Mode');

			return [ mode, window.Blockly.Arduino.ORDER_ATOMIC ];
		};
/*
		// Initializing some customized blocks
		window.Blockly.Blocks['component_getter'] = {
			init: function() {
				this.setColour(230);
				this.appendValueInput('PIN', 'Number')
					.appendField('讀取模塊');
				this.setOutput(true, 'Number');
				//this.setPreviousStatement(true, null);
				//this.setNextStatement(true, null);
			}
		};

		window.Blockly.Arduino['component_getter'] = function() {
			var pin = window.Blockly.Arduino.valueToCode(this, 'PIN', window.Blockly.Arduino.ORDER_ATOMIC);

//			if (module.info.hasOwnProperty('methods')) {
//				if (!module.info.methods['getter'])
//					return;

//				Blockly.Arduino.setups_['setup_serial_' + profile.default.serial] = 'Serial.begin(' + profile.default.serial + ');\n';

//			}

			console.log(pin);
			var code = 'digitalRead(' + pin  + ')';

			return [ code, window.Blockly.Arduino.ORDER_ATOMIC ];
		};
*/

		// Notify view that time to update
		this.dispatch('store.Blockly', 'change');
	});

	this.on('store.Blockly.updateAvailModule', function *(moduleId, pin) {

		updateAvailableModule(moduleId, pin);
		this.dispatch('store.Blockly', 'change');
	});

	this.on('store.Blockly.resetAndApplySample', function *(sample) {
		var store = this.getState('Blockly');
		
		// Reset
		store.registered = {};

		for (var index in sample.modules) {
			var module = sample.modules[index];
			updateAvailableModule(module.sku, module.pin);
		}

		this.dispatch('store.Blockly', 'change');

		setTimeout(function() {
			var xml = window.Blockly.Xml.textToDom(sample.code);
			window.Blockly.mainWorkspace.clear();
			window.Blockly.Xml.domToWorkspace(store.workspace, xml);
			window.Blockly.fireUiEvent(window, 'resize');
		}, 1000);
	});
};
