<commit_before>require 'colors'
path       = require 'path'
fs         = require 'fs'
libxml     = require 'libxmljs'
spawn      = require('child_process').spawn
ini        = require 'ini'


class $

  @onTaskNamespace: (taskName, scope) ->
    names = taskName.split(':')
    taskName = names.pop()
    rec = ->
      if names.length == 0
        scope taskName
      else
        namespace names.shift(), rec
    rec()

  @mem: (fn) -> (args ...)->
    (arguments.callee['memo'] or= {})[[].concat args] or= fn.apply this, args

  @pathSearch: (bin, dirs = process.env.PATH.split(':')) ->
    return path.join(d, bin) for d in dirs when fs.existsSync path.join(d, bin)

  @_: (args ...) -> path.join.apply(path, [process.cwd()].concat(args))

  @E: (args ...) -> path.join.apply(path, [__dirname, '../etc'].concat(args))

  @pkg: JSON.parse fs.readFileSync path.join(__dirname, '../package.json'), 'utf-8'

  @main: -> require('./tintan/main') Tintan

  @tasks: (tintan)-> require('./tintan/tasks') tintan

  @home: @mem ->
    dirs = [process.env.TI_HOME
            '/Library/Application Support/Titanium'
            path.join(process.env.HOME, 'Library/Application Support/Titanium')
            path.join(process.env.HOME, '.titanium')
    ]
    return d for d in dirs when fs.existsSync d

  @os: {
    'linux':  'linux'
    'darwin': 'osx'
    'win32':  'win'
  }[require('os').platform()]

  @ios_version: @mem ->
    iphone_dir = path.join(process.env.HOME, 'Library', 'Application Support', 'iPhone Simulator')
    if fs.existsSync iphone_dir
      ver = process.env.IOS_VERSION or Tintan.config().get 'ios_version'
      if ver and fs.existsSync(path.join(iphone_dir, ver))
        ver
      else
        fs.readdirSync(iphone_dir).sort()[-1..][0]

  @android_version: @mem ->
    android_dir = path.join(@android_home(), 'platforms')
    if fs.existsSync android_dir
      ver = process.env.ANDROID_VERSION or Tintan.config().get 'android_version'
      if ver and fs.existsSync path.join(android_dir, 'android-' + (ver - 1))
        ver
      else
        (d.split('-')[1] for d in fs.readdirSync(android_dir)).sort((a,b)-> a - b)[-1..][0]

  @android_home: @mem ->
    dir = process.env.ANDROID_SDK or Tintan.config().get 'android_sdk'
    if dir and fs.existsSync dir
      dir
    else
      brew_location = {
        'osx': '/usr/local/Cellar/android-sdk'
        'linux': '/opt/android-sdk'
        'win': 'C:\\Program Files (x86)\\Android\\android-sdk'
        }[@os]
      if fs.existsSync brew_location
        path.join brew_location, fs.readdirSync(brew_location).sort()[-1..][0]

  @platform: @mem ->
    process.env.TI_PLATFORM or Tintan.config().get 'ti_platform' or {osx: 'iphone'}[@os] or 'android'

  @sdk: @mem ->
    dir = process.env.TI_SDK or Tintan.config().get('ti_sdk') or Tintan.appXML().sdk()
    if dir and fs.existsSync path.join(@home(), 'mobilesdk', @os, dir)
      dir
    else
      fs.readdirSync(path.join(@home(), 'mobilesdk', @os)).sort()[-1..][0]

  @py: @mem ->
    return py for py in [process.env.PYTHON, process.env.TI_PYTHON,
                         process.env.PYTHON_EXECUTABLE, Tintan.config().get 'ti_python'] when fs.existsSync py
    @pathSearch 'python'

  @titan: (args ...)->
    @tipy.apply(this, [['titanium.py'], args])

  @fastdev: (args ...)=> @titan.apply this, ['fastdev'].concat(args or [])

  @tipy: (ary, args ..., cb)->
    unless cb instanceof Function
      if args?.length then args.push cb else args = cb
      cb = ->
    tool = path.join.apply(path, [@home(), 'mobilesdk', @os, @sdk()].concat(ary))

    opts = {}
    tf = (-> return true if /testflight/.test name for name in jake.program.taskNames)()
    if Tintan.config().get('compile_js') is false
      opts.env = 'SKIP_JS_MINIFY': true
      opts.env[k] = v for k, v of process.env

    p = spawn @py(), [tool].concat(args), opts
    p.stdout.on 'data', (data)-> process.stdout.write data
    p.stderr.on 'data', (data)-> process.stderr.write data
    p.on 'exit', cb

  @gitrepo: @mem ->
    gitconfig = ini.parse fs.readFileSync $._('.git/config'), 'utf-8'
    repo = gitconfig['remote "origin"']?.url
    unless repo
      remote = (k for k of gitconfig when /^remote/.test k).sort()[0]
      repo = gitconfig[remote].url if remote
    {type: 'git', url: repo} or {}


class Config

  DEFAULT_OPTIONS =
    verbose: true
    iced: false
    compile_js: true
    debug: false
    debug_address: '127.0.0.1'
    debug_port: 5858
    android_avd: null
    android_device: ''
    android_sdk: null
    keystore: null
    storepass: null
    key_alias: null
    ios_version: null
    ti_home: null
    ti_platform: null
    ti_python: null
    ti_sdk: null
    sublime_project: null

  file: -> $._('tintan.config')

  load: ->
    @options = {} if !fs.existsSync(@file())
    @options ?= JSON.parse(fs.readFileSync(@file(), 'utf-8'))
    @options[k] = v for k,v of DEFAULT_OPTIONS when !@options.hasOwnProperty(k)
    @save()

  save: ->
    if @options
      fs.writeFileSync(@file(), JSON.stringify(@options, undefined, 2), 'utf-8')
    else
      console.log('Nothing to save.')

  init: ->
    @load()
    @options[k] = v for k,v of DEFAULT_OPTIONS when !@options.hasOwnProperty(k)
    @save()

  display: ->
    @load()
    console.log(k + ': ' + v) for k, v of @options when @options.hasOwnProperty(k)

  set: (opts = {}) ->
    @load()
    for k, v of opts
      if @options.hasOwnProperty(k)
        v = DEFAULT_OPTIONS[k] if v is 'default'
        @options[k] = v
        console.log('' + k + ' set to ' + v)
        @save()
      else
        console.log('Unknown option: ' + k)

  get: (option) ->
    @load()
    result = null
    if @options.hasOwnProperty(option)
      if @options[option] is 'true'
        result = true
      else if @options[option] is 'false'
        result = false
      else
        result = @options[option]
    return result

  promptForNext: (i) =>
    if i < 0
      return
    key = @config_opts[i]['k']
    value = @config_opts[i]['v']
    process.stdout.write(key + ':' + value + ', new value: ')
    process.stdin.resume()
    process.stdin.on('data', (text) =>
      process.stdin.removeAllListeners('data')
      text = text.replace(/(\r\n|\n|\r)/gm,"")
      ans = text
      if typeof(@options[key]) is 'boolean'
        lowerText = '' + text.toLowerCase()<commit_msg>Colorful config terminal output<commit_after>require 'colors'
path       = require 'path'
fs         = require 'fs'
libxml     = require 'libxmljs'
spawn      = require('child_process').spawn
ini        = require 'ini'


class $

  @onTaskNamespace: (taskName, scope) ->
    names = taskName.split(':')
    taskName = names.pop()
    rec = ->
      if names.length == 0
        scope taskName
      else
        namespace names.shift(), rec
    rec()

  @mem: (fn) -> (args ...)->
    (arguments.callee['memo'] or= {})[[].concat args] or= fn.apply this, args

  @pathSearch: (bin, dirs = process.env.PATH.split(':')) ->
    return path.join(d, bin) for d in dirs when fs.existsSync path.join(d, bin)

  @_: (args ...) -> path.join.apply(path, [process.cwd()].concat(args))

  @E: (args ...) -> path.join.apply(path, [__dirname, '../etc'].concat(args))

  @pkg: JSON.parse fs.readFileSync path.join(__dirname, '../package.json'), 'utf-8'

  @main: -> require('./tintan/main') Tintan

  @tasks: (tintan)-> require('./tintan/tasks') tintan

  @home: @mem ->
    dirs = [process.env.TI_HOME
            '/Library/Application Support/Titanium'
            path.join(process.env.HOME, 'Library/Application Support/Titanium')
            path.join(process.env.HOME, '.titanium')
    ]
    return d for d in dirs when fs.existsSync d

  @os: {
    'linux':  'linux'
    'darwin': 'osx'
    'win32':  'win'
  }[require('os').platform()]

  @ios_version: @mem ->
    iphone_dir = path.join(process.env.HOME, 'Library', 'Application Support', 'iPhone Simulator')
    if fs.existsSync iphone_dir
      ver = process.env.IOS_VERSION or Tintan.config().get 'ios_version'
      if ver and fs.existsSync(path.join(iphone_dir, ver))
        ver
      else
        fs.readdirSync(iphone_dir).sort()[-1..][0]

  @android_version: @mem ->
    android_dir = path.join(@android_home(), 'platforms')
    if fs.existsSync android_dir
      ver = process.env.ANDROID_VERSION or Tintan.config().get 'android_version'
      if ver and fs.existsSync path.join(android_dir, 'android-' + (ver - 1))
        ver
      else
        (d.split('-')[1] for d in fs.readdirSync(android_dir)).sort((a,b)-> a - b)[-1..][0]

  @android_home: @mem ->
    dir = process.env.ANDROID_SDK or Tintan.config().get 'android_sdk'
    if dir and fs.existsSync dir
      dir
    else
      brew_location = {
        'osx': '/usr/local/Cellar/android-sdk'
        'linux': '/opt/android-sdk'
        'win': 'C:\\Program Files (x86)\\Android\\android-sdk'
        }[@os]
      if fs.existsSync brew_location
        path.join brew_location, fs.readdirSync(brew_location).sort()[-1..][0]

  @platform: @mem ->
    process.env.TI_PLATFORM or Tintan.config().get 'ti_platform' or {osx: 'iphone'}[@os] or 'android'

  @sdk: @mem ->
    dir = process.env.TI_SDK or Tintan.config().get('ti_sdk') or Tintan.appXML().sdk()
    if dir and fs.existsSync path.join(@home(), 'mobilesdk', @os, dir)
      dir
    else
      fs.readdirSync(path.join(@home(), 'mobilesdk', @os)).sort()[-1..][0]

  @py: @mem ->
    return py for py in [process.env.PYTHON, process.env.TI_PYTHON,
                         process.env.PYTHON_EXECUTABLE, Tintan.config().get 'ti_python'] when fs.existsSync py
    @pathSearch 'python'

  @titan: (args ...)->
    @tipy.apply(this, [['titanium.py'], args])

  @fastdev: (args ...)=> @titan.apply this, ['fastdev'].concat(args or [])

  @tipy: (ary, args ..., cb)->
    unless cb instanceof Function
      if args?.length then args.push cb else args = cb
      cb = ->
    tool = path.join.apply(path, [@home(), 'mobilesdk', @os, @sdk()].concat(ary))

    opts = {}
    tf = (-> return true if /testflight/.test name for name in jake.program.taskNames)()
    if Tintan.config().get('compile_js') is false
      opts.env = 'SKIP_JS_MINIFY': true
      opts.env[k] = v for k, v of process.env

    p = spawn @py(), [tool].concat(args), opts
    p.stdout.on 'data', (data)-> process.stdout.write data
    p.stderr.on 'data', (data)-> process.stderr.write data
    p.on 'exit', cb

  @gitrepo: @mem ->
    gitconfig = ini.parse fs.readFileSync $._('.git/config'), 'utf-8'
    repo = gitconfig['remote "origin"']?.url
    unless repo
      remote = (k for k of gitconfig when /^remote/.test k).sort()[0]
      repo = gitconfig[remote].url if remote
    {type: 'git', url: repo} or {}


class Config

  DEFAULT_OPTIONS =
    verbose: true
    iced: false
    compile_js: true
    debug: false
    debug_address: '127.0.0.1'
    debug_port: 5858
    android_avd: null
    android_device: ''
    android_sdk: null
    keystore: null
    storepass: <PASSWORD>
    key_alias: null
    ios_version: null
    ti_home: null
    ti_platform: null
    ti_python: null
    ti_sdk: null
    sublime_project: null

  file: -> $._('tintan.config')

  load: ->
    @options = {} if !fs.existsSync(@file())
    @options ?= JSON.parse(fs.readFileSync(@file(), 'utf-8'))
    @options[k] = v for k,v of DEFAULT_OPTIONS when !@options.hasOwnProperty(k)
    @save()

  save: ->
    if @options
      fs.writeFileSync(@file(), JSON.stringify(@options, undefined, 2), 'utf-8')
    else
      console.log('Nothing to save.')

  init: ->
    @load()
    @options[k] = v for k,v of DEFAULT_OPTIONS when !@options.hasOwnProperty(k)
    @save()

  display: ->
    @load()
    console.log(k + ': ' + ('' + v).yellow) for k, v of @options when @options.hasOwnProperty(k)

  set: (opts = {}) ->
    @load()
    for k, v of opts
      if @options.hasOwnProperty(k)
        v = DEFAULT_OPTIONS[k] if v is 'default'
        @options[k] = v
        console.log('' + k + ' set to '.green + v)
        @save()
      else
        console.log('Unknown option: '.red + k)

  get: (option) ->
    @load()
    result = null
    if @options.hasOwnProperty(option)
      if @options[option] is 'true'
        result = true
      else if @options[option] is 'false'
        result = false
      else
        result = @options[option]
    return result

  promptForNext: (i) =>
    if i < 0
      return
    key = @config_opts[i]['k']
    value = @config_opts[i]['v']
    process.stdout.write(key + ':' + value + ', new value: ')
    process.stdin.resume()
    process.stdin.on('data', (text) =>
      process.stdin.removeAllListeners('data')
      text = text.replace(/(\r\n|\n|\r)/gm,"")
      ans = text
      if typeof(@options[key]) is 'boolean'
        lowerText = '' + text.toLowerCase()
{'fraction_non_alphanumeric': 0.1688352406333281, 'fraction_numerical': 0.005016460260228876, 'mean_word_length': 2.6216292932160092, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 72, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 4}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5151454', 'n_tokens_mistral': 4510, 'n_tokens_neox': 4404, 'n_words': 1048}