


models.common = function() {

  models.User = Backbone.Model.extend({
    defaults: {
      auth:false
    },

    initialize: function() {
      _.bindAll(this,'load','logout','register','login','store','access','checkemail')    
    },

    
    store: function( user, login ) {
      var self=this

      self.set({auth:true,user:user,login:login})
      store.set('user',{user:user,login:login})
      util.http_cookies[C.token_name] = login.token
      self._access = null

      if( user.anon ) {
        var anon_purchases = store.get('anon_purchases') || []
        anon_purchases = anon_purchases.list ? anon_purchases.list : anon_purchases
        user.editions = anon_purchases.concat(user.editions)
        store.set('anon_purchases',user.editions)
      }
    },


    load: function(cb) {
      var self=this

      var userdetails = store.get('user') || {}

      if( userdetails.user ) {
        self.store(userdetails.user,userdetails.login)
        self.trigger('user-load')
      }
                                   
      // always get latest user details
      util.http_get('/api/auth/user',function(err,res){

        if( res.auth ) {

          if( !res.user.anon ) {
            var anon_purchases = store.get('anon_purchases') || []
            //anon_purchases = anon_purchases.list ? anon_purchases.list : anon_purchases

            if( 0 < anon_purchases.length ) {
              res.user.editions = _.uniq(anon_purchases.concat(res.user.editions||[]))

              util.http_post('/api/auth/editions',{editions:res.user.editions},function(err,res){
                if( res && res.ok ) {
                  store.set('anon_purchases',[])
                }
              })
            }
          }

          self.store(res.user,res.login)
        }

        self.trigger('user-load')
        cb && cb(res.auth,res.user,res.login)
      })
    },


    register: function(data,cb) {
      util.http_post('/api/auth/register',data,function(err,res){
        cb && cb(err,res)
      })
    },


    save: function(data,cb) {
      util.http_post('/api/auth/user',data,function(err,res){
        cb && cb(err,res)
      })
    },


    logout: function(cb) {
      var self=this

      util.http_post('/api/auth/logout',{},function(err,res){
        if( err ) log(err);
        self.set({auth:false,user:false,login:false});
        delete self._access
        store.remove('user')
        self.trigger('user-logout');
        cb && cb();
      })
    },

    login: function(data,cb) {
      var self=this

      data.app = app.sbpappid

      util.http_post('/api/auth/login',data,function(err,res){
        cb(err,res)
      })
    },

    access: function() {
      var self=this;

      if( self._access ) {
        return self._access
      }

      var user = self.get('user')
      if( user ) {
        self._access = new business.Access(user.editions||[])
      }
      else {
        self._access = new business.Access([])
      }

      return self._access
    },

    checkemail: function(email,cb) {
      util.http_post('/api/auth/checkemail',{email:email},function(err,res){
        cb(err,res)
      })
    },

    remind: function(email,cb) {
      util.http_post('/api/auth/remind',{email:email},function(err,res){
        cb(err,res)
      })
    }

  })



  models.Page = Backbone.Model.extend({
    name:'Page',

    defaults: {
      type: 'none'
    },

    initialize: function() {
      _.bindAll(this,'load','start','prev')
      var self=this

      self.cache = util.makecache(11)
      
      self.stack = []
    },

    load: function( cb ) {
      var self=this;

      var page = self.get('page')
      if( page ) {
        var content = self.cache.get(page)
        if( content ) {
          cb( page, content )
        }
        else {
          $.get('/c/page/'+page+'.html',function(content){
            if(content){
              self.cache.set( page, content )
              cb(page,content)
            }
            else {
              cb()
            }
          })
        }
      }
      else cb()
    },

    start: function() {
      var self=this
      self.trigger('page-start')
    },


    prev: function( page ) {
      var self=this
      if( page ) {
        if( page !== self.stack[self.stack.length-1] ) {
          self.stack.push(page)
        }
      }
      else {
        return self.stack.pop()
      }
    }
  })



  models.Edition = Backbone.Model.extend({
    name:'Edition',

    defaults: {
      code:'daily'
    },

    initialize: function() {
      _.bindAll(this)
      var self=this
    }
  })



  models.FormFactor = Backbone.Model.extend({
    defaults: {
      w: 320, 
      h: 480,
      vh: 381,
      bh: 99,
      type:C.SMALL
    },

    initialize: function() {
      _.bindAll(this,'setDim','discover')
      var self=this;

      if( navigator.device ) {
        document.addEventListener('orientationchange',function(){self.discover()},true)
      }
      else {
        var orient = true
        if( bowser.msie ) {
          orient = 9 <= bowser.version
        }

        if( orient ) {
          window.addEventListener('orientationchange',function(){self.discover()},true)
        }
      }

      function calc_vh() {
        var h = self.get('h')
        var bh = self.get('bh')
        self.set({vh:h-bh})
      }

      self.bind('change:h',calc_vh)
      self.bind('change:bh',calc_vh)

      self.discover()
    },

    discover: function() {
      this.setDim(window.innerWidth,window.innerHeight)
    },

    // work out visible height, allowing for chrome hiding via scrollTo trick
    setDim: function(w,h) {
      var type = w<=680?C.SMALL:C.LARGE
      var orient

      //if( !navigator.device ) {
        h = h+1
      //}

      if( undefined !== window.orientation ) {
        orient = (0 === window.orientation % 180) ? 'port' : 'land'
      }
      else {
        orient = w<h?'port':'land'
      }

      if( !navigator.device && !navigator.standalone && !C.dev ) {
        if( !this.origorient ) {
          this.origorient = orient
          h += 60
        }
        else if( 'port' === orient ) {
          h += 60
        }
      }

      this.set({w:w,h:h,orient:orient,
                type:type})

      this.trigger('layout')
    }
  })


  models.ArticleStore = Backbone.Model.extend({
    initialize: function() {
      _.bindAll(this,'load','loadcat','formatDate','prep','clear','parseDate','fixDates')
      var self=this

      self.cache = util.makecache(1111,'persist','art_',C.version)
      self.catcache = util.makecache(1111,'persist','cat_',C.version)
      self.subcatcache = util.makecache(1111,'persist','subcat_',C.version)

      self.dirty = {
        cat:{},subcat:{}
      }

      // always reload if not present
      self.cached = {}
    },

    load: function( sid, cb ) {                                             
      var self=this
      var article = self.cache.get(sid)

      if( article ) {
        if( !article.contents ) {
          article = self.prep(article)
          self.cache.set(sid,article)
        }
        cb(article)
      }
      else {
        util.http_get( '/api/art/'+encodeURIComponent(sid), function(err,article) {
          if(err) log(err);
          if( article && article.h ) {
            var art = self.prep(article)
            self.cache.set(sid,article)
            if( 'mob' !== mode.name ) {
              app.view.articleview.setArticle(art)
            }
            cb(art)
          }
          else {
            cb(null)
          }
        })
      }
    },


    clear: function( sid ) {
      var self=this;

      if( sid ) {
        self.dirty.cat[sid]=true
        self.dirty.subcat[sid]=true
        self.cache.del( sid )
      }
      else {
        self.cached = {}

        self.cache.clear()
        self.catcache.clear()
        self.subcatcache.clear()
      }
    },


    prep: function(article) {
      var self=this

      var pubdate = self.parseDate(article.pub)

      var format = 'daily'==article.ty ? 'time' : ''
      var meta = self.formatDate(pubdate,format)+' by '+article.aut
      var contents = [
        {type:'head',
         title:article.h,
         meta:meta,
         img:(article.i&&article.i[0]&&article.i[0].s3in),
         ic:(article.i&&article.i[0]&&article.i[0].ic),
         premium:('daily'!=article.ty)
        }
      ]

      if( article.pg ) {
        var ad_placed = false
        for( var i = 0; i < article.pg.length; i++ ) {
          var text = article.pg[i].p
          var cd = {
            text:text,
            type:'para'
          }

          if( 'client' === C.markdown ) {
            if( text && _.isString(text) && 'undefined' !== typeof(markdown) ) {
              try {
                cd.html = markdown.toHTML(text)
              }
              catch( e ) {
                cd.html = cd.text
              }
            }
          }
          else if( 'server' === C.markdown ) {
            cd.html = cd.text
          }

          contents.push(cd)            


          /* 
          if( 'pad' === mode.name ) {
            if( 16 < article.pg.length && (i==10 || i==20) ) {
	      ad_placed=true
              contents.push({type:'adpad',index:i})
            }
            else if( 8 < article.pg.length && (i==4) ) {
	      ad_placed=true
              contents.push({type:'adpad',index:i})
            }
          }
          */
        }

        if( !ad_placed ) {
          contents.push({type:'adpad',index:i})
        }
      }


      /*
      if( false && 'pad' === mode.name && !navigator.device ) {
        contents.push({type:'fillone'})
        contents.push({type:'filltwo'})
      }
      */


      contents = contents.concat([
        {type:'tail',text:'share'},
        {type:'tail',text:'similar'}])
      
      var art = {
        id:article.sid,
        sid:article.sid,
        h:article.h,
        d:article.d,
        c:article.c,
        sc:article.sc,
        m:meta,
        pub:pubdate,
        ty:article.ty,
        contents:contents,
        prepped:true
      }

      return art
    },

    formatDate: function(date,format) {
      return date ? 
        ( 'time'==format ? date.toString('HH:mm') : 
          'datetime'==format ? date.toString('HH:mm, d MMMM yyyy') : 
          date.toString('d MMMM yyyy') ) 
      : '----'
      
    },


    // full ISO format
    parseDate: function( datestr ) {
      var dstr = datestr
      if( slang.endsWith(dstr,'.000Z') ) {
        dstr = dstr.substring(0,datestr.length-5)
      }
      else if( slang.endsWith(dstr,'Z') ) {
        dstr = dstr.substring(0,datestr.length-1)
      }

      if( '.' == dstr.charAt(dstr.length-4) ) {
        dstr = dstr.substring(0,dstr.length-4)
      }

      var d = Date.parseExact(dstr,'yyyy-MM-ddTHH:mm:ss')
      var fixtz = d.getTimezoneOffset() * 60000
      d.setTime( d.getTime() - fixtz )
      return d
    },


    fixDates: function(artlist) {
      var self=this;
      for( var i = 0; i < artlist.length; i++ ) {
        var art = artlist[i]
        art.pub = 'string'==typeof(art.pub)?self.parseDate(art.pub):art.pub
        art.ts  = 'string'==typeof(art.ts)?self.parseDate(art.ts):art.ts
      }
    },


    loadcat: function(cat,subcat,cb) {
      var self=this

      var ec = (app.model.edition.get('code') || 'daily')

      var callback = cb
      var type = 'subcat'
      var cache = self.subcatcache
      var key   = ec+'_'+subcat

      if( !callback || '' === subcat || null === subcat ) {
        callback = cb || subcat
        type = 'cat'
        cache = self.catcache
        key   = ec+'_'+cat
      }

      var dirty = self.dirty[type]

      var arts = cache.get(key)

      // check for dirty stories
      if( arts && arts.list) {
        var list = arts.list
        for( var i = 0; i < list.length; i++ ) {
          var sid = list[i].sid

          if( dirty[sid] ) {
            delete dirty[sid]
            arts = null
          }
        }
      }

      // new stories
      if( 0 < _.keys(dirty).length ) {
        arts = null
        dirty = {}
      }

      
      if( !self.cached[key] ) {
        arts = null
      }

      if( arts && arts.list) {
        self.fixDates(arts.list)
        cb && cb(arts.list)
      }
      else {
        var ecparam = ("daily"!=ec) ? ("?edition="+ec) : ''
        var path = 
          ( 'subcat'==type ?
            '/api/subcat/'+(cat?encodeURIComponent(cat)+'/':'')+encodeURIComponent(subcat)+'/art' : 
            '/api/cat/'+encodeURIComponent(cat)+'/art'
          )+ecparam

        util.http_get( 
          path,
          function(err,data) {
            if( err ) return log(err);

            self.fixDates(data.list)

            cache.set(key,{when:new Date().getTime(),list:data.list})
            self.cached[key]=true

            cb && cb(data.list)
          }
        )
      }
    },

    // halt if cb returns false
    multiload: function(artids,cb) {
      var self=this;
      var run = true

      var cached  = []
      var loadids = []
      for( var i = 0; i < artids.length; i++ ) {
        var artid = artids[i]
        var art = self.cache.get(artid)
        if( art ) {
          if( !art.prepped ) {
            art = self.prep(art)
            self.cache.set(art.sid,art)
          }
          cached.push(art)
        }
        else {
          loadids.push(artid)
        }
      }

      if( 0 < cached.length ) {
        run = cb(cached)
        if( !run ) return;
      }

      if( 0 < loadids.length ) {
        var idliststr = (''+loadids)
        util.http_get( 
          '/api/art/list?'+idliststr,
          function(err,data) {
            if( err ) return log(err);
            if( data && data.list ) {
              var prepped = []
              for( var i = 0; i < data.list.length; i++ ) {
                var art = data.list[i]

                var prepart = self.prep(art)
                self.cache.set(art.sid,prepart)
                prepped.push(prepart)
              }
              cb(prepped)
            }
          }
        )
      }
    }
  })



  models.ArticleLayout = Backbone.Model.extend({
    defaults: {
      curcol:  0,
      numcols: 1,
      colsize: 1,
      width:   320,
      height:  480,
      viewwidth:   320,
      viewheight:  480,
      colwidth:   320,
      colheight:  400,
      offset: 0
    },


    initialize: function() {
      _.bindAll(this,'setFormFactor');//,'swipeLeft','swipeRight')    

      var self=this
      this.bind('flowend',function(lastcol){
        var numcols = 1+lastcol
        var colwidth = self.get('colwidth')
        var width = 1+(numcols*colwidth)
        this.set({width:width})
        this.set({numcols:numcols})
      })
    },


    setFormFactor: function(formfactor) {
      var colsize = Math.max(1,Math.floor( formfactor.w / 340 ))

      var topbar = 44

      var adjust = 200//146//126

      var colwidth = Math.floor( formfactor.w / colsize )
      if( !mode.touch ) {
        colwidth = 680;
      }

      this.set({
        width:  formfactor.w,
        height: formfactor.h,
        viewwidth:  formfactor.w,
        viewheight: formfactor.h,
        colsize:    colsize,
        colheight:  500,//formfactor.h - C.len - topbar - adjust,
        colwidth:   colwidth
      })

      //this.trigger('layout')
    }
  })





  models.ColItem = Backbone.Model.extend({
    defaults: {
      type: 'para',
      text: ''
    }
  })



  models.Search = Backbone.Model.extend({
    defaults: {
      results: []
    },

    initialize: function() {
      _.bindAll(this,'query','page')    
      var self=this;
      self.cache = util.makecache(0)
    },

    query: function(terms,cb) {
      var self=this;

      self.full_article_list = null
      self.set({terms:terms})

      function handle_results( list ) {
        if( list ) {
          self.full_article_list = list
          self.set({article_list:list})
          self.trigger('article-list-update')
          cb && cb(list)
        }
      }

      var list = self.cache.get(terms)
      if( list ) {
        handle_results(list)
      }
      else {
        util.http_get('/api/art/search?q='+terms, function(err,data){
          if( data && data.list ) {
            handle_results(data.list)
            self.cache.set(terms,data.list)
          }
        })
      }
    },

    page: function(pagecount,maxcount) {
      var self=this;

      var start = pagecount * maxcount
      
      var page = []
      for(var i = start; i < start+maxcount; i++) {
        page.push(self.full_article_list[i])
      }

      self.set({article_list:page})
      self.trigger('article-list-update')
    }
  });


  models.Similar = Backbone.Model.extend({
    defaults: {
      results: []
    },

    initialize: function() {
      _.bindAll(this,'query')    
    },

    query: function(id, cb) 
    {
      cb()

      if( false ) {
        var self=this
        self.set({id:id})
        util.http_get('/api/art/similar?id='+encodeURIComponent(id), function(err, data)
                      {
                        if (data && data.list) 
                        {
                          app.articleview.setSimilar && app.articleview.setSimilar(data.list)
                          cb()
                        }
                      })
      }

      return;
    }
  });



  models.FrontArticles = Backbone.Model.extend({
    defaults: {
      article_list: []
    },


    initialize: function() {
      _.bindAll(this,'load')    
      var self=this
    },


    load: function(whence) {
      var self=this

      var ec = app.model.edition.get('code')
      var cat = self.catmodel.get('cat')
      var subcat = self.catmodel.get('subcat')

      function loadarts( prepend ) {
        var hidden = {}
        if( prepend ) {
          prepend.forEach(function(art){
            hidden[art.sid]=1
          })
        }

        app.model.articlestore.loadcat(cat,subcat,function(list){
          if( prepend ) {
            var i

            for( i = 0; i < list.length; i++ ) {
              if( hidden[list[i].sid] ) {
                delete hidden[list[i].sid];
                i--
              }
            }

            for( i = prepend.length-1; -1 < i; i-- ) {
              var art = prepend[i]
              for( var j = 0; j < list.length; j++ ) {
                if( list[j].sid === art.sid ) {
                  list.splice(j,1)
                  j--
                }
              }
              list.unshift(art)
            }
          }

          self.set({article_list:list})
          self.trigger('article-list-update')
        })
      }


      // FIX move to server
      var now = new Date()
      var do_priorities = 0!==now.getDay() && ('daily'===ec||null===ec) && 'Home' === cat && ('News' === subcat || '' === subcat || null === subcat)

      function priority(list) {
        self.set({article_list:list})
        self.trigger('article-list-update')
        loadarts(list)
      }

      if( do_priorities ) {

        var data
        if( !app.model.articlestore.dirty.priority ) {
          data = app.model.articlestore.catcache.get('Priority')
        }

        if( data && data.list ) {
          app.model.articlestore.fixDates(data.list)
          priority(data.list)
        }
        else {
          util.http_get('/api/art/priority',function(err,data){
            if( data && data.list ) {
              app.model.articlestore.fixDates(data.list)
              app.model.articlestore.catcache.set('Priority',{list:data.list,when:new Date().getTime()})
              app.model.articlestore.dirty.priority = false
              priority(data.list)
            }
          })
        }
      }
      else {
        loadarts()
      }
    }
  })


  models.Settings = Backbone.Model.extend({
    defaults: {
      bright:   5,
      textsize: 5,
      swipemsgcount: 5
    },

    initialize: function() {
      _.bindAll(this,'save')    
      var self=this

      var settings = store.get('settings') || {}

      if( !settings.saved ) {
        self.save()
      }
      else {
        self.set({
          bright:   settings.bright,
          textsize: settings.textsize,
          swipemsgcount: settings.swipemsgcount
        })
      }
    },

    save: function() {
      var self=this;

      var settings = {
        saved:true,
        bright:self.get('bright'),
        textsize:self.get('textsize'),
        swipemsgcount:self.get('swipemsgcount')
      }

      store.set('settings',settings)
    }

  })




  models.Overview = Backbone.Model.extend({
    name:'Overview',

    initialize: function() {
      _.bindAll(this,'load')    
    },

    load: function(cb) {
      var self=this

      util.http_get('/api/cat/overview',function(err,data){
        if( data && data.list ) {
          self.set({overview:data.list})
          cb && cb(data.list)
        }
      })
    }
  })



  models.FeatureBox = Backbone.Model.extend({

    initialize: function() {
      _.bindAll(this,'load')    
    },

    load: function() {
      var self=this

      var edition = app.model.edition.get('code')
      if( null===edition||'daily'===edition ) {
        edition = ''
      }

      util.http_get('/api/feat/box?max=18&edition='+edition,function(err,data){
        if( data && data.list ) {
          self.set({article_list:data.list})
          self.trigger('article-list-update')
        }
      })
    }
  })


  models.FeatureBar = Backbone.Model.extend({
    defaults: {
      article_list: []
    },

    initialize: function() {
      _.bindAll(this,'load')    
    },

    load: function() {
      var self=this

      var max = 'port' === app.model.formfactor.get('orient') ? 3 : 4

      util.http_get('/api/feat/top?max='+max,function(err,data){
        if( data && data.list ) {
          self.set({article_list:data.list})
          self.trigger('article-list-update')
        }
      })
    }
  })


  models.Purchase = Backbone.Model.extend({
    defaults: {
    },

    initialize: function() {
      _.bindAll(this,'begin')    
      var self=this
    },

    begin: function(spec) {
      var self=this;

      if( 'article' === spec.type ) {
        app.model.articlestore.load(spec.sid,function(story){
          var eddate  = business.editiondate(story.pub)
          var edition = business.yyyymmdd(eddate) 
          self.set({edition:edition,sid:spec.sid})
          self.trigger('begin')
        })
      }
      else {
        self.set({edition:spec.edition})
        self.trigger('begin')
      }
    }
  })

}




var ColItemList = Backbone.Collection.extend({
  model: models.ColItem
})


