/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/*
TODO:
 - Standalone app save/load.
 - "Create Gadget" functionality.
 - textarea <-> text editor communication (extension?)
 - Convert to GWT?
*/

var IS_GADGET = window['gadgets'] || false;

var MARKUP_AREA = 'markupArea';
var JS_AREA = 'javascriptArea';
var TEMPLATES_AREA = 'templatesArea';
var CSS_AREA = 'cssArea';

var DEFAULT_MARKUP =
  '<script type="text/os-template">\n' + 
  '  <!-- Your markup here -->\n' +
  '</script>';

var DEFAULT_JS =
  'var TEST_data = {\n' + 
  '  // Your test data here.\n' +
  '};\n' +
  '\n' +
  'function TEST_main() {\n' +
  '  // Your init code here.\n' +
  '}';
  
var DEFAULT_CSS = '';

var DEFAULT_OS_DATA = {
  'Viewer' : { 
    'id': 'kenny', 
    'displayName': 'Kenny', 
    'profileUrl': 'http://en.wikipedia.org/wiki/Kenny_McCormick',
    'thumbnailUrl': 'http://upload.wikimedia.org/wikipedia/en/thumb/6/6b/Kenny.svg/200px-Kenny.svg.png'
  },
  'ViewerFriends' : [
    {
      'id': 'stanmarsh',
      'displayName': 'Stan Marsh',
      'profileUrl': 'http://en.wikipedia.org/wiki/Stan_Marsh',
      'thumbnailUrl': 'http://upload.wikimedia.org/wikipedia/en/thumb/0/05/StanMarsh.svg/130px-StanMarsh.svg.png'
    },
    {
      'id': 'kylebroflovski',
      'displayName': 'Kyle Broflovski',
      'profileUrl': 'http://en.wikipedia.org/wiki/Kyle_Broflovski',
      'thumbnailUrl': 'http://upload.wikimedia.org/wikipedia/en/thumb/1/18/Kyle.svg/200px-Kyle.svg.png'
    },
    {
      'id': 'ericcartman',
      'displayName': 'Eric Cartman',
      'profileUrl': 'http://en.wikipedia.org/wiki/Eric_Cartman',
      'thumbnailUrl': 'http://upload.wikimedia.org/wikipedia/en/thumb/5/5d/Eric.svg/180px-Eric.svg.png'
    }
  ]
}; 

var CSS_numRules = 0;

var apps = {
  user: [],
  samples: []
};

function log(msg) {
  if (window.console) {
    window.console.log(msg);
  }
}

function error(msg) {
  if (window.console) {
    window.console.error(msg);
  }
}

function makeServerRequest(url, callback) {
  if (IS_GADGET) {
    var params = {};
    params[gadgets.io.RequestParameters.CONTENT_TYPE] =
        gadgets.io.ContentType.JSON;
    var url = 'http://ostemplates-devapp.appspot.com' + url;
    gadgets.io.makeRequest(url, function(obj) {
      callback(obj.text);
    }, params);
  } else {
    var req = null;
    if (typeof(XMLHttpRequest) != "undefined") {
      req = new XMLHttpRequest();
    } else {
      req = new ActiveXObject("MSXML2.XMLHTTP");
    }
    req.open("GET", url, true);
    req.onreadystatechange = function() {
      if (req.readyState == 4) {
        callback(req.responseText);
      }
    };
    req.send(null);
  }
}

function saveUserApps() {
  if (!IS_GADGET) {
    error('Save failed: Unsupported for non-gadget.');
    return;
  }
  var req = opensocial.newDataRequest();
  var data = apps.user;
  req.add(req.newUpdatePersonAppDataRequest('VIEWER', 'apps', data));
  req.send(loadUserApps);
}

function loadUserApps() {
  if (!IS_GADGET) {
    var template = os.getTemplate('apps-template');
    template.renderInto(document.getElementById('apps'), apps.user);
    return;
  }
  var req = opensocial.newDataRequest();
  var fields = ['apps'];
  req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER),
      'viewer');
  var idSpec = opensocial.newIdSpec(
      {'userId':'VIEWER'});      
  req.add(req.newFetchPersonAppDataRequest(idSpec, fields), 'viewer_data');
  req.send(function(data) {
    var viewer = data.get('viewer').getData();
    apps.user = [];
    if (viewer) {
      var viewer_data = data.get('viewer_data').getData();
      if (viewer_data[viewer.getId()]) {
        var str = gadgets.util.unescapeString(viewer_data[viewer.getId()].apps);
        apps.user = gadgets.json.parse(str);      
      }      
    }
    if (apps.user.length) {
      apps.user.sort();
    }
    var template = os.getTemplate('apps-template');
    template.renderInto(document.getElementById('apps'), apps.user);
  });
}

function deleteUserApps() {
  if (confirm("Delete all saved applications?")) {
    apps.user = [];
    saveUserApps();    
  }
}

function loadSampleApps() {
  // Fetch the sample apps from the server.
  makeServerRequest('/samples', function(response) {
    apps.samples = eval(response);
    var template = os.getTemplate('samples-template');
    template.renderInto(document.getElementById('samples'), apps.samples);  
  });
}

function loadApps() {
  loadUserApps();
  loadSampleApps();
}

function getOption(optionName) {
  return document.optionsForm[optionName];
}

function mergeData(from, to) {
  if (!from) {
    return;
  }
  for (var key in from) {
    to[key] = from[key];
  }
  // Basically, if this is an OpenSocial ResponseItem, then remove any
  // test data with the same name.
  for (var key in to) {
    if (from.get && from.get(key) && from.get(key).getData()) {
      delete to[key];
    }
  }
}

function fetchData(callback) {
  if (!window.opensocial || !window.opensocial.newDataRequest) {
    // OpenSocial API unavailable, just return the sample data.
    callback(DEFAULT_OS_DATA);
    return;
  }
  var req = opensocial.newDataRequest();
  req.add(req.newFetchPersonRequest('VIEWER'), 'Viewer');
  var idSpecFriends = opensocial.newIdSpec(
      {'userId':'VIEWER', 'groupId':'FRIENDS'});
  req.add(req.newFetchPeopleRequest(idSpecFriends), 'ViewerFriends');

  req.add(req.newFetchPersonRequest('OWNER'), 'Owner');
  var idSpecFriends = opensocial.newIdSpec(
      {'userId':'OWNER', 'groupId':'FRIENDS'});
  req.add(req.newFetchPeopleRequest(idSpecFriends), 'OwnerFriends');

  req.send(function(data) {
    callback(data);
  });
}

function clearUserCode() {
  // Clear dynamically created head elements.
  var node = os.Loader.headNode_.firstChild;
  var nodesToClear = [];
  for (; node; node = node.nextSibling) {
    if (!node.tagName)
      continue;
    // All non-src'd script tags are stripped out.
    if (node.tagName.toLowerCase() == 'script' &&
      node.getAttribute('type') == 'text/javascript' &&
      !node.getAttribute('src')) {
      nodesToClear.push(node);
    }
  }  
  while (nodesToClear.length) {
    var node = nodesToClear.pop();
    os.Loader.headNode_.removeChild(node);
  }
  
  // Clear JS.
  window.TEST_main = null;
  window.TEST_data = {};
  
  // Clear CSS.
  if (document.styleSheets.length) {
    var sheet = document.styleSheets[0];
    var numRules = sheet.insertRule ?
        sheet.cssRules.length : sheet.rules.length;
    while (numRules > CSS_numRules) {
      if (sheet.insertRule) {
        sheet.deleteRule(numRules - 1);
      } else {
        sheet.removeRule(numRules - 1);
      }
      --numRules;
    }
  }
}

function runApp(opt_data) {
  // Clear any JS/CSS from previous app.
  clearUserCode();
  
  // Load user javascript.
  var jsCode = getCode(JS_AREA);
  os.Loader.injectJavaScript(jsCode);

  // Load user CSS.
  var cssCode = getCode(CSS_AREA);
  os.Loader.injectStyle(cssCode);
  
  // Insert the template data into the DOM.
  var xmlData = getCode(MARKUP_AREA);
  var div = document.getElementById('output');
  
  // This is a workaround for IE found by kjin. Apparently IE discards
  // any dynamic innerHTML content if only <script> tags are found.
  div.innerHTML = '<span>&nbsp</span>' + xmlData;
  div.removeChild(div.firstChild);
  
  // Need to clear all cached inline templates for re-processing.
  os.Container.inlineTemplates_ = [];
  
  // Merge the user's test data and any incoming optional data.
  var data = window.TEST_data;
  mergeData(opt_data, data);
  
  // Before templates have been processed, run the user's javascript.
  if (window.TEST_main) {
    window.TEST_main();
  } else {
    os.Container.processDocument(data);    
  }
}

function execute() {
  if (getOption('fetch').checked) {
    fetchData(runApp);
    return;
  }
  runApp();
}

function saveApp() {
  var appData = {};
  appData.js = getCode(JS_AREA);
  appData.markup = getCode(MARKUP_AREA);
  appData.css = getCode(CSS_AREA);
  appData.name = document.detailsForm.name.value;
  appData.fetchOSData = getOption('fetch').checked;
  if (!appData.name) {
    alert('Name required to save app.');
    return;
  }
  for (var k in apps.user) {
    if (apps.user[k].name == appData.name) {
      alert('App named \'' +
            appData.name +
            '\' already exists.');
      return;
    }
  }
  document.detailsForm.name.value = '';
  apps.user.push(appData);
  saveUserApps();
}

function setAppData(appData) {
  appData.markup = appData.markup || '';
  appData.js = appData.js || '';
  appData.css = appData.css || '';
  setCode(MARKUP_AREA, appData.markup);
  setCode(JS_AREA, appData.js);
  setCode(CSS_AREA, appData.css);
  getOption('fetch').checked = appData.fetchOSData || false;
  var noteDiv = document.getElementById('app_note');
  if (noteDiv) {
    noteDiv.innerHTML = appData.note || '';
  }
  try {
    execute();
  } catch(e) {
    error(e);
  }
}

function setUserApp(index) {
  setAppData(apps.user[index]);
}

function setSample(category, index) {
  setAppData(apps.samples[category].samples[index]);
}

function showTab(tabIndex) {
  var i = 0;
  var max = 20;
  for (var i = 0; i < 20; ++i) {
    var tab = document.getElementById('tabx' + i);
    var tabContent = document.getElementById('tabContent' + i);
    if (!tab || !tabContent) {
      break;
    }
    if (i == tabIndex) {
      tab.className = 'tabx-tab tabx-selected';
      tabContent.className = '';
    }
    else {
      tab.className = 'tabx-tab tabx-unselected';
      tabContent.className = 'invisible';
    }
  }
}

function getCode(area) {
  if (!window['CodePress']) {
    return document.getElementById(area).value;
  }
  return window[area].getCode();
}

function setCode(area, code) {
  if (!window['CodePress']) {
    document.getElementById(area).value = code;
    return;
  }
  window[area].setCode(code);
  // Workaround because just setting will disable the highlighting.
  window[area].toggleEditor();
  window[area].toggleEditor();  
}

function shortenName(name) {
  if (name.length > 20) {
    name = name.substr(17) + '...';
  }
  return name;
}

function resetAll() {
  // Setup some default code and run it.
  //document.getElementById('markup').value = DEFAULT_MARKUP;
  setCode(MARKUP_AREA, DEFAULT_MARKUP);
  setCode(JS_AREA, DEFAULT_JS);
  setCode(CSS_AREA, DEFAULT_CSS);
  getOption('fetch').checked = false;
  showTab(0);  
  execute();
}

function init() {
  showTab(0);
  
  // Cache the number of rules in order to reset back when running
  // an app.
  if (document.styleSheets.length) {
    var sheet = document.styleSheets[0];
    if (sheet.insertRule) {
      CSS_numRules = sheet.cssRules.length;
    } else {
      CSS_numRules = sheet.rules.length;
    }
  }
  
  if (!window['CodePress']) {
    document.getElementById('markupArea').disabled = false;
    document.getElementById('javascriptArea').disabled = false;
    document.getElementById('cssArea').disabled = false;
  }
    
  // Create a "test" namespace for the user.
  // TODO(davidbyttow): Use a different URL? Does it even matter?
  os.createNamespace('test', 'http://code.google.com/');
  // Process our app templates.
  os.Container.processDocument();

  // Set loading text.
  var template = os.getTemplate('loading-template');
  template.renderInto(document.getElementById('samples'));  
  template.renderInto(document.getElementById('apps'));  

  loadApps();
  resetAll();
}
