/*
 * This file is part of TbSync.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
 */
 
"use strict";

var { ExtensionParent } = ChromeUtils.importESModule(
  "resource://gre/modules/ExtensionParent.sys.mjs"
);

var tbsyncExtension = ExtensionParent.GlobalManager.getExtension(
  "tbsync@jobisoft.de"
);
var { TbSync } = ChromeUtils.importESModule(
  `chrome://tbsync/content/tbsync.sys.mjs?${tbsyncExtension.manifest.version}`
);

ChromeUtils.defineESModuleGetters(this, {  
  CalAlarm: "resource:///modules/CalAlarm.sys.mjs",
  CalAttachment: "resource:///modules/CalAttachment.sys.mjs",
  CalAttendee: "resource:///modules/CalAttendee.sys.mjs",
  CalEvent: "resource:///modules/CalEvent.sys.mjs",
  CalTodo: "resource:///modules/CalTodo.sys.mjs",
}); 
  
var lightning = {

  cal: null,
  ICAL: null,
   
  load: async function () {
    try {
      TbSync.lightning.cal = ChromeUtils.importESModule("resource:///modules/calendar/calUtils.sys.mjs").cal;
      TbSync.lightning.ICAL = ChromeUtils.importESModule("resource:///modules/calendar/Ical.sys.mjs").default;
      let manager = TbSync.lightning.cal.manager;
      manager.addCalendarObserver(this.calendarObserver);
      manager.addObserver(this.calendarManagerObserver);
    } catch (e) {
      TbSync.dump("Check4Lightning","Error during lightning module import: " + e.toString() + "\n" + e.stack);
      Components.utils.reportError(e);
    }
  },

  unload: async function () {
    //removing global observer
    let manager = TbSync.lightning.cal.manager;
    manager.removeCalendarObserver(this.calendarObserver);
    manager.removeObserver(this.calendarManagerObserver);
  },




  
  // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  // * AdvancedTargetData, an extended TargetData implementation, providers
  // * can use this as their own TargetData by extending it and just
  // * defining the extra methods
  // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  
  AdvancedTargetData : class {
    constructor(folderData) {            
      this._folderData = folderData;
      this._targetObj = null;           
    }
    
    // Check, if the target exists and return true/false.
    hasTarget() {
      let calManager = TbSync.lightning.cal.manager;
      let target = this._folderData.getFolderProperty("target");
      let calendar = calManager.getCalendarById(target);
      
      return calendar ? true : false;
    }

    // Returns the target obj, which TbSync should return as the target. It can
    // be whatever you want and is returned by FolderData.targetData.getTarget().
    // If the target does not exist, it should be created. Throw a simple Error, if that
    // failed.
    async getTarget() {
      let calManager = TbSync.lightning.cal.manager;
      let target = this._folderData.getFolderProperty("target");
      let calendar = calManager.getCalendarById(target);
      
      if (!calendar) {
        calendar = await TbSync.lightning.prepareAndCreateCalendar(this._folderData);
        if (!calendar)
          throw new Error("notargets");
      }

      if (!this._targetObj || this._targetObj.id != calendar.id)
        this._targetObj = new TbSync.lightning.TbCalendar(calendar, this._folderData);

      return this._targetObj;
    }
    
    /**
     * Removes the target from the local storage. If it does not exist, return
     * silently. A call to ``hasTarget()`` should return false, after this has
     * been executed.
     *
     */
    removeTarget() {
      let calManager = TbSync.lightning.cal.manager;
      let target = this._folderData.getFolderProperty("target");
      let calendar = calManager.getCalendarById(target);

      try {
        if (calendar) {
          calManager.removeCalendar(calendar);
        }
      } catch (e) {}
      TbSync.db.clearChangeLog(target);
      this._folderData.resetFolderProperty("target");             
    }


    /**
     * Disconnects the target in the local storage from this TargetData, but
     * does not delete it, so it becomes a stale "left over" . A call
     * to ``hasTarget()`` should return false, after this has been executed.
     * 
     */
    disconnectTarget() {
      let calManager = TbSync.lightning.cal.manager;
      let target = this._folderData.getFolderProperty("target");
      let calendar = calManager.getCalendarById(target);

      if (calendar) {
        let changes = TbSync.db.getItemsFromChangeLog(target, 0, "_by_user");        
        if (changes.length > 0) {
          this.targetName = this.targetName + " (*)";
        }
        calendar.setProperty("disabled", true);
        calendar.setProperty("tbSyncProvider", "orphaned");
        calendar.setProperty("tbSyncAccountID", "");
      }
      TbSync.db.clearChangeLog(target);
      this._folderData.resetFolderProperty("target");        
    } 
    
    set targetName(newName) {
      let calManager = TbSync.lightning.cal.manager;
      let target = this._folderData.getFolderProperty("target");
      let calendar = calManager.getCalendarById(target);

      if (calendar) {
        calendar.name = newName;
      } else {
        throw new Error("notargets");
      }
    }
  
    get targetName() {
      let calManager = TbSync.lightning.cal.manager;
      let target = this._folderData.getFolderProperty("target");
      let calendar = calManager.getCalendarById(target);

      if (calendar) {
        return calendar.name;
      } else {
        throw new Error("notargets");
      }
    }

    setReadOnly(value) {
      // hasTarget() can throw an error, ignore that here
      try {
        if (this.hasTarget()) {
          this.getTarget().then(target => target.calendar.setProperty("readOnly", value));
        }
      } catch (e) {
        Components.utils.reportError(e);
      }
    }

    
    // * * * * * * * * * * * * * * * * *
    // * AdvancedTargetData extension  * 
    // * * * * * * * * * * * * * * * * *
    
    get isAdvancedCalendarTargetData() {
      return true;
    }
    
    get folderData() {
      return this._folderData;
    }
    
    // The calendar target does not support a custom primaryKeyField, because
    // the lightning implementation only allows to search for items via UID.
    // Like the addressbook target, the calendar target item element has a
    // primaryKey getter/setter which - however - only works on the UID.
    
    // enable or disable changelog
    get logUserChanges(){
      return true;
    }

    calendarObserver(aTopic, tbCalendar, aPropertyName, aPropertyValue, aOldPropertyValue) {
      switch (aTopic) {
        case "onCalendarPropertyChanged":
          //Services.console.logStringMessage("["+ aTopic + "] " + tbCalendar.calendar.name + " : " + aPropertyName);
          break;

        case "onCalendarDeleted":
        case "onCalendarPropertyDeleted":
          //Services.console.logStringMessage("["+ aTopic + "] " +tbCalendar.calendar.name);
          break;
      }
    }

    itemObserver(aTopic, tbItem, tbOldItem) {
      switch (aTopic) {
        case "onAddItem":
        case "onModifyItem":
        case "onDeleteItem":
          //Services.console.logStringMessage("["+ aTopic + "] " + tbItem.nativeItem.title);
          break;
      }
    }

    // replace this with your own implementation to create the actual addressbook,
    // when this class is extended
    async createCalendar(newname) {
      let calManager = TbSync.lightning.cal.manager;
      let newCalendar = calManager.createCalendar("storage", Services.io.newURI("moz-storage-calendar://"));
      newCalendar.id = TbSync.lightning.cal.getUUID();
      newCalendar.name = newname;
      return newCalendar
    }      
    
  },




  
  // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  // * TbItem and TbCalendar Classes
  // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  
  TbItem : class {
    constructor(TbCalendar, item) {
      if (!TbCalendar)
        throw new Error("TbItem::constructor is missing its first parameter!");

      if (!item)
        throw new Error("TbItem::constructor is missing its second parameter!");

      this._tbCalendar = TbCalendar;
      this._item = item;
      
      this._isTodo = item.isTodo ? item.isTodo() : (item instanceof Ci.calITodo);
      this._isEvent = item.isEvent ? item.isEvent() :  (item instanceof Ci.calIEvent);
    }
    
    get tbCalendar() {
      return this._tbCalendar;
    }

    get isTodo() {
      return this._isTodo;
    }
    
    get isEvent() {
      return this._isEvent;
    }




    
    get nativeItem() {
      return this._item;
    }

    get UID() {
      return this._item.id;
    }
    
    get primaryKey() {
      // no custom key possible with lightning, must use the UID
      return this._item.id;
    }

    set primaryKey(value) {
      // no custom key possible with lightning, must use the UID
      this._item.id = value;   
    }

    clone() {
      return new TbSync.lightning.TbItem(this._tbCalendar, this._item.clone());
    }

    toString() {
      return this._item.icalString;
    }

    getProperty(property, fallback = "") {
      return this._item.hasProperty(property) ? this._item.getProperty(property) : fallback;
    }

    setProperty(property, value) {
      this._item.setProperty(property, value);
    }
    
    deleteProperty(property) {
      this._item.deleteProperty(property);
    }
        
    get changelogData() {         
      return TbSync.db.getItemDataFromChangeLog(this._tbCalendar.UID, this.primaryKey);
    }

    get changelogStatus() {
      return TbSync.db.getItemStatusFromChangeLog(this._tbCalendar.UID, this.primaryKey);
    }

    set changelogStatus(status) {
      let value = this.primaryKey;
      
      if (value) {
        if (!status) {
          TbSync.db.removeItemFromChangeLog(this._tbCalendar.UID, value);
          return;
        }

        if (this._tbCalendar.logUserChanges || status.endsWith("_by_server")) {
          TbSync.db.addItemToChangeLog(this._tbCalendar.UID, value, status);
        }
      }
    }
  },


  TbCalendar : class {
    constructor(calendar, folderData) {
      this._calendar = calendar;
      this._folderData = folderData;
     }

    get calendar() {
      return this._calendar;
    }
    
    get promisifyCalendar() {
      return this._calendar;
    }

    get logUserChanges() {
      return this._folderData.targetData.logUserChanges;
    }
    
    get primaryKeyField() {
      // Not supported by lightning. We let the implementation sit here, it may get changed in the future.
      // In order to support this, lightning needs to implement a proper getItemfromProperty() method.
      return null;
    }
    
    get UID() {
      return this._calendar.id;
    }

    createNewEvent() {
      let event = new CalEvent();
      return new TbSync.lightning.TbItem(this, event);
    }
    
    createNewTodo() {
      let todo = new CalTodo();
      return new TbSync.lightning.TbItem(this, todo);
    }

    
    
    
    async addItem(tbItem, pretagChangelogWithByServerEntry = true) {
      if (this.primaryKeyField && !tbItem.getProperty(this.primaryKeyField)) {
        tbItem.setProperty(this.primaryKeyField, this._folderData.targetData.generatePrimaryKey());
        //Services.console.logStringMessage("[TbCalendar::addItem] Generated primary key!");
      }
      
      if (pretagChangelogWithByServerEntry) {
        tbItem.changelogStatus = "added_by_server";
      }
      return await this._calendar.addItem(tbItem._item);
    }
    
    async modifyItem(tbNewItem, tbOldItem, pretagChangelogWithByServerEntry = true) {
      // only add entry if the current entry does not start with _by_user
      let status = tbNewItem.changelogStatus ? tbNewItem.changelogStatus : "";
      if (pretagChangelogWithByServerEntry && !status.endsWith("_by_user")) {
        tbNewItem.changelogStatus = "modified_by_server";
      }
      
      return await this._calendar.modifyItem(tbNewItem._item, tbOldItem._item); 
    }        
    
    async deleteItem(tbItem, pretagChangelogWithByServerEntry = true) {
      if (pretagChangelogWithByServerEntry) {
        tbItem.changelogStatus = "deleted_by_server";
      }
      return await this._calendar.deleteItem(tbItem._item); 
    }
    
    // searchId is interpreted as the primaryKeyField, which is the UID for this target
    async getItem (searchId) {
      let item = await this._calendar.getItem(searchId); 
      if (item) {
        return new TbSync.lightning.TbItem(this, item);
      }
      return null;
    }

    async getItemFromProperty(property, value) {
      if (property == "UID") return await this.getItem(value);
      else throw ("TbSync.lightning.getItemFromProperty: Currently onle the UID property can be used to search for items.");
    }

    async getAllItems() {
      return await this._calendar.getItems(Ci.calICalendar.ITEM_FILTER_ALL_ITEMS, 0, null, null);
    }
  
    getAddedItemsFromChangeLog(maxitems = 0) {             
      return TbSync.db.getItemsFromChangeLog(this.calendar.id, maxitems, "added_by_user").map(item => item.itemId);
    }

    getModifiedItemsFromChangeLog(maxitems = 0) {             
      return TbSync.db.getItemsFromChangeLog(this.calendar.id, maxitems, "modified_by_user").map(item => item.itemId);
    }
    
    getDeletedItemsFromChangeLog(maxitems = 0) {             
      return TbSync.db.getItemsFromChangeLog(this.calendar.id, maxitems, "deleted_by_user").map(item => item.itemId);
    }
    
    getItemsFromChangeLog(maxitems = 0) {             
      return TbSync.db.getItemsFromChangeLog(this.calendar.id, maxitems, "_by_user");
    }

    removeItemFromChangeLog(id, moveToEndInsteadOfDelete = false) {             
      TbSync.db.removeItemFromChangeLog(this.calendar.id, id, moveToEndInsteadOfDelete);
    }
    
    clearChangelog() {
      TbSync.db.clearChangeLog(this.calendar.id);
    }
  },
  



  
  // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  // * Internal Functions
  // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  
  getFolderFromCalendarUID: function(calUID) {
    let folders = TbSync.db.findFolders({"target": calUID});
    if (folders.length == 1) {
      let accountData = new TbSync.AccountData(folders[0].accountID);
      return new TbSync.FolderData(accountData, folders[0].folderID);
    }
    return null;
  },
  
  getFolderFromCalendarURL: function(calURL) {
    let folders = TbSync.db.findFolders({"url": calURL});
    if (folders.length == 1) {
      let accountData = new TbSync.AccountData(folders[0].accountID);
      return new TbSync.FolderData(accountData, folders[0].folderID);
    }
    return null;
  },  
  
  calendarObserver : { 
    onStartBatch : function () {},
    onEndBatch : function () {},
    onLoad : function (aCalendar) {},
    onError : function (aCalendar, aErrNo, aMessage) {},

    onAddItem : function (aAddedItem) { 
      if (!(aAddedItem && aAddedItem.calendar))
        return;

      let folderData = TbSync.lightning.getFolderFromCalendarUID(aAddedItem.calendar.id);                    
      if (folderData 
        && folderData.targetData 
        && folderData.targetData.isAdvancedCalendarTargetData) {

        let tbCalendar = new TbSync.lightning.TbCalendar(aAddedItem.calendar, folderData);
        let tbItem = new TbSync.lightning.TbItem(tbCalendar, aAddedItem);          
        let itemStatus = tbItem.changelogStatus;

        // if this card was created by us, it will be in the log
        if (itemStatus && itemStatus.endsWith("_by_server")) {
          let age = Date.now() - tbItem.changelogData.timestamp;
          if (age < 1500) {
            // during freeze, local modifications are not possible
            return;
          } else {
            // remove blocking entry from changelog after freeze time is over (1.5s),
            // and continue evaluating this event
            abItem.changelogStatus = "";
          }
        } 

        if (itemStatus == "deleted_by_user")  {
          // deleted ?  user moved item out and back in -> modified
          tbItem.changelogStatus = "modified_by_user";
        } else {
          tbItem.changelogStatus = "added_by_user";
        }
        
        if (tbCalendar.logUserChanges) TbSync.core.setTargetModified(folderData);
        folderData.targetData.itemObserver("onAddItem", tbItem, null);                                        
      }
    },

    onModifyItem : function (aNewItem, aOldItem) {
      //check, if it is a pure modification within the same calendar
      if (!(aNewItem && aNewItem.calendar && aOldItem && aOldItem.calendar && aNewItem.calendar.id == aOldItem.calendar.id))
        return;

      let folderData = TbSync.lightning.getFolderFromCalendarUID(aNewItem.calendar.id);                    
      if (folderData 
        && folderData.targetData 
        && folderData.targetData.isAdvancedCalendarTargetData) {

        let tbCalendar = new TbSync.lightning.TbCalendar(aNewItem.calendar, folderData);
        let tbNewItem = new TbSync.lightning.TbItem(tbCalendar, aNewItem);          
        let tbOldItem = new TbSync.lightning.TbItem(tbCalendar, aOldItem);          
        let itemStatus = tbNewItem.changelogStatus;
          
        // if this card was created by us, it will be in the log
        if (itemStatus && itemStatus.endsWith("_by_server")) {
          let age = Date.now() - tbNewItem.changelogData.timestamp;
          if (age < 1500) {
            // during freeze, local modifications are not possible
            return;
          } else {
            // remove blocking entry from changelog after freeze time is over (1.5s),
            // and continue evaluating this event
            tbNewItem.changelogStatus = "";
          }
        } 

        if (itemStatus != "added_by_user") {
          //added_by_user -> it is a local unprocessed add do not re-add it to changelog
          tbNewItem.changelogStatus = "modified_by_user";
        }

        if (tbCalendar.logUserChanges) TbSync.core.setTargetModified(folderData);
        folderData.targetData.itemObserver("onModifyItem", tbNewItem, tbOldItem);                                        
      }
    },

    onDeleteItem : function (aDeletedItem) {
      if (!(aDeletedItem && aDeletedItem.calendar))
        return;

      let folderData = TbSync.lightning.getFolderFromCalendarUID(aDeletedItem.calendar.id);                    
      if (folderData 
        && folderData.targetData 
        && folderData.targetData.isAdvancedCalendarTargetData) {

        let tbCalendar = new TbSync.lightning.TbCalendar(aDeletedItem.calendar, folderData);
        let tbItem = new TbSync.lightning.TbItem(tbCalendar, aDeletedItem);
        let itemStatus = tbItem.changelogStatus;

        // if this card was created by us, it will be in the log
        if (itemStatus && itemStatus.endsWith("_by_server")) {
          let age = Date.now() - tbItem.changelogData.timestamp;
          if (age < 1500) {
            // during freeze, local modifications are not possible
            return;
          } else {
            // remove blocking entry from changelog after freeze time is over (1.5s),
            // and continue evaluating this event
            tbItem.changelogStatus = "";
          }
        } 

        if (itemStatus == "added_by_user") {
          //a local add, which has not yet been processed (synced) is deleted -> remove all traces
          tbItem.changelogStatus = "";
        } else {
          tbItem.changelogStatus = "deleted_by_user";
        }

        if (tbCalendar.logUserChanges) TbSync.core.setTargetModified(folderData);
        folderData.targetData.itemObserver("onDeleteItem", tbItem, null);
      }
    },

    //Changed properties of the calendar itself (name, color etc.)
    onPropertyChanged : function (aCalendar, aName, aValue, aOldValue) {
      let folderData = TbSync.lightning.getFolderFromCalendarUID(aCalendar.id);                    
      if (folderData 
        && folderData.targetData 
        && folderData.targetData.isAdvancedCalendarTargetData) {

        let tbCalendar = new TbSync.lightning.TbCalendar(aCalendar, folderData);
          
        switch (aName) {
          case "color":
            // update stored color to recover after disable
            folderData.setFolderProperty("targetColor", aValue); 
            break;
          case "name":
            // update stored name to recover after disable
            folderData.setFolderProperty("targetName", aValue);                         
            // update settings window, if open
            Services.obs.notifyObservers(null, "tbsync.observer.manager.updateSyncstate", folderData.accountID);                    
            break;
        }
        
        folderData.targetData.calendarObserver("onCalendarPropertyChanged", tbCalendar, aName, aValue, aOldValue);                
      }
    },

    //Deleted properties of the calendar itself (name, color etc.)
    onPropertyDeleting : function (aCalendar, aName) {
      let folderData = TbSync.lightning.getFolderFromCalendarUID(aCalendar.id);                    
      if (folderData 
        && folderData.targetData 
        && folderData.targetData.isAdvancedCalendarTargetData) {

        let tbCalendar = new TbSync.lightning.TbCalendar(aCalendar, folderData);
          
        switch (aName) {
          case "color":
          case "name":
            //update settings window, if open
            Services.obs.notifyObservers(null, "tbsync.observer.manager.updateSyncstate", folderData.accountID);                    
          break;
        }

        folderData.targetData.calendarObserver("onCalendarPropertyDeleted", tbCalendar, aName);                
      }
    }
  },

  calendarManagerObserver : {
    onCalendarRegistered : function (aCalendar) {              
    },
    
    onCalendarUnregistering : function (aCalendar) {
      /*let folderData = TbSync.lightning.getFolderFromCalendarUID(aCalendar.id);                    
      if (folderData 
        && folderData.targetData 
        && folderData.targetData.isAdvancedCalendarTargetData) {

        folderData.targetData.calendarObserver("onCalendarUnregistered", aCalendar);                
      }*/
    },
      
    onCalendarDeleting : async function (aCalendar) {
      let folderData = TbSync.lightning.getFolderFromCalendarUID(aCalendar.id);                    
      if (folderData 
        && folderData.targetData 
        && folderData.targetData.isAdvancedCalendarTargetData) {

        // If the user switches "offline support", the calendar is deleted and recreated. Thus,
        // we wait a bit and check, if the calendar is back again and ignore the delete event.
        if (aCalendar.type == "caldav") {
          await TbSync.tools.sleep(1500);
          let calManager = TbSync.lightning.cal.manager;
          for (let calendar of calManager.getCalendars({})) {
            if (calendar.uri.spec == aCalendar.uri.spec) {
              // update the target
              folderData.setFolderProperty("target", calendar.id)
              return;
            }
          }
        }
        
        //delete any pending changelog of the deleted calendar
        TbSync.db.clearChangeLog(aCalendar.id);			

        let tbCalendar = new TbSync.lightning.TbCalendar(aCalendar, folderData);
          
        //unselect calendar if deleted by user and update settings window, if open
        if (folderData.getFolderProperty("selected")) {
          folderData.setFolderProperty("selected", false);
          //update settings window, if open
          Services.obs.notifyObservers(null, "tbsync.observer.manager.updateSyncstate", folderData.accountID);
        }
        
        folderData.resetFolderProperty("target");
        folderData.targetData.calendarObserver("onCalendarDeleted", tbCalendar);                

      }
    },
  },

  
  
  //this function actually creates a calendar if missing
  prepareAndCreateCalendar: async function (folderData) {       
    let calManager = TbSync.lightning.cal.manager;
    let provider = folderData.accountData.getAccountProperty("provider");

    //check if  there is a known/cached name, and use that as starting point to generate unique name for new calendar 
    let cachedName = folderData.getFolderProperty("targetName");                         
    let newname = cachedName == "" ? folderData.accountData.getAccountProperty("accountname") + " (" + folderData.getFolderProperty("foldername") + ")" : cachedName;

    //check if there is a cached or preloaded color - if not, chose one
    if (!folderData.getFolderProperty("targetColor")) {
      //define color set
      let allColors = [
        "#3366CC",
        "#DC3912",
        "#FF9900",
        "#109618",
        "#990099",
        "#3B3EAC",
        "#0099C6",
        "#DD4477",
        "#66AA00",
        "#B82E2E",
        "#316395",
        "#994499",
        "#22AA99",
        "#AAAA11",
        "#6633CC",
        "#E67300",
        "#8B0707",
        "#329262",
        "#5574A6",
        "#3B3EAC"];
      
      //find all used colors
      let usedColors = [];
      for (let calendar of calManager.getCalendars({})) {
        if (calendar && calendar.getProperty("color")) {
          usedColors.push(calendar.getProperty("color").toUpperCase());
        }
      }

      //we do not want to change order of colors, we want to FILTER by counts, so we need to find the least count, filter by that and then take the first one
      let minCount = null;
      let statColors = [];
      for (let i=0; i< allColors.length; i++) {
        let count = usedColors.filter(item => item == allColors[i]).length;
        if (minCount === null) minCount = count;
        else if (count < minCount) minCount = count;

        let obj = {};
        obj.color = allColors[i];
        obj.count = count;
        statColors.push(obj);
      }
      
      //filter by minCount
      let freeColors = statColors.filter(item => (minCount == null || item.count == minCount));
      folderData.setFolderProperty("targetColor", freeColors[0].color);        
    }
    
    //create and register new calendar
    let newCalendar = await folderData.targetData.createCalendar(newname);
    newCalendar.setProperty("tbSyncProvider", provider);
    newCalendar.setProperty("tbSyncAccountID", folderData.accountData.accountID);

    //store id of calendar as target in DB
    folderData.setFolderProperty("target", newCalendar.id); 
    folderData.setFolderProperty("targetName", newCalendar.name);
    folderData.setFolderProperty("targetColor",  newCalendar.getProperty("color"));
    return newCalendar;        
  }
}
