const fs = require("fs")
const path = require('path')
const parser = require('iptv-playlist-parser')
const axios = require('axios')
const zlib = require("zlib")
const DOMParser = require('xmldom').DOMParser
const urlParser = require('url')

const supportedCategories = [ 'Auto','Business', 'Classic','Comedy','Documentary','Education','Entertainment', 'Family','Fashion','Food', 'General', 'Health', 'History', 'Hobby', 'Kids', 'Legislative','Lifestyle','Local', 'Movies', 'Music', 'News', 'Quiz', 'Religious','Sci-Fi', 'Shop', 'Sport', 'Travel', 'Weather', 'XXX' ]

const blacklist = [
  '80.80.160.168', // repeats on a loop
  '63.237.48.3', // not a live stream
  '189.216.247.113', // not working streams
]

let cache = {}

class Playlist {
  constructor(data) {
    this.attrs = {
      'x-tvg-url': data.tvg.url
    }

    this.items = data.items
  }

  getHeader() {
    let parts = ['#EXTM3U']
    for(let key in this.attrs) {
      let value = this.attrs[key]
      if(value) {
        parts.push(`${key}="${value}"`)
      }
    }

    return `${parts.join(' ')}\n`
  }
}

class Channel {
  constructor(data) {
    this.id = data.tvg.id
    this.name = data.tvg.name
    this.logo = data.tvg.logo
    this.group = this._getGroup(data.group.title)
    this.url = data.url
    this.title = data.name
  }

  _getGroup(groupTitle) {
    if(!groupTitle) return ''
      
    const groupIndex = supportedCategories.map(g => g.toLowerCase()).indexOf(groupTitle.toLowerCase())

    if(groupIndex === -1) {
      groupTitle = ''
    } else {
      groupTitle = supportedCategories[groupIndex]
    }

    return groupTitle
  }

  toString() {
    const info = `-1 tvg-id="${this.id}" tvg-name="${this.name}" tvg-logo="${this.logo}" group-title="${this.group}",${this.title}`

    return '#EXTINF:' + info + '\n' + this.url + '\n'
  }
}

function parsePlaylist(filename) {
  const content = readFile(filename)
  const result = parser.parse(content)

  return new Playlist(result)
}

function createChannel(data) {
  return new Channel(data)
}

async function loadEPG(url) {
  const data = await getGzipped(url)
  const doc = new DOMParser().parseFromString(data, 'text/xml')
  const channelElements = doc.getElementsByTagName('channel')
  let channels = {}
  for(let i = 0; i < channelElements.length; i++) {
    let channel = {}
    let channelElement = channelElements[i]
    channel.id = channelElement.getAttribute('id')
    channel.names = []
    for(let nameElement of Object.values(channelElement.getElementsByTagName('display-name'))) {
      if(nameElement.firstChild) {
        channel.names.push(nameElement.firstChild.nodeValue)
      }
    }
    channel.names = channel.names.filter(n => n)
    const iconElements = channelElement.getElementsByTagName('icon')
    if(iconElements.length) {
      channel.icon = iconElements[0].getAttribute('src')
    }

    channels[channel.id] = channel
  }

  return Promise.resolve({ 
    url, 
    channels 
  })
}

function getGzipped(url) {
  const supportedTypes = ['application/x-gzip', 'application/octet-stream']

  return new Promise((resolve, reject) => {
    var buffer = []
    axios({
      method: 'get',
      url: url,
      responseType:'stream'
    }).then(res => {
      let stream
      if(supportedTypes.indexOf(res.headers['content-type']) > -1) {
        let gunzip = zlib.createGunzip()         
        res.data.pipe(gunzip)
        stream = gunzip
      } else {
        stream = res.data        
      }

      stream.on('data', function(data) {
        buffer.push(data.toString())
      }).on("end", function() {
        resolve(buffer.join(""))
      }).on("error", function(e) {
        reject(e)
      })
    }).catch(e => {
      reject(e)
    })
  })
}

function byTitleAndUrl(a, b) {
  var titleA = a.title.toLowerCase()
  var titleB = b.title.toLowerCase()
  var urlA = a.url.toLowerCase()
  var urlB = b.url.toLowerCase()
  
  if(titleA < titleB) return -1
  if(titleA > titleB) return 1

  if(urlA < urlB) return -1
  if(urlA > urlB) return 1

  return 0
}

function sortByTitleAndUrl(arr) {
  return arr.sort(byTitleAndUrl)
}

function readFile(filename) {
  return fs.readFileSync(path.resolve(__dirname) + `/../${filename}`, { encoding: "utf8" })
}

function appendToFile(filename, data) {
  fs.appendFileSync(path.resolve(__dirname) + '/../' + filename, data)
}

function createFile(filename, data) {
  fs.writeFileSync(path.resolve(__dirname) + '/../' + filename, data)
}

function getBasename(filename) {
  return path.basename(filename, path.extname(filename))
}

function addToCache(url) {
  let id = getUrlPath(url)

  cache[id] = true
}

function checkCache(url) {
  let id = getUrlPath(url)

  return cache.hasOwnProperty(id)
}

function clearCache() {
  cache = {}
}

function getUrlPath(u) {
  let parsed = urlParser.parse(u)
  let searchQuery = parsed.search || ''
  let path = parsed.host + parsed.pathname + searchQuery

  return path.toLowerCase()
}

function validateUrl(channelUrl) {
  const url = new URL(channelUrl)
  const host = url.hostname

  return blacklist.indexOf(host) === -1
}

function skipPlaylist(filename) {
  let testCountry = process.env.npm_config_country
  let excludeList = process.env.npm_config_exclude
  let excludeCountries = excludeList ? excludeList.split(',') : []
  
  if (testCountry && filename !== 'channels/' + testCountry + '.m3u') return true
  
  for(const countryCode of excludeCountries) {
    if (filename === 'channels/' + countryCode + '.m3u') return true
  }

  return false
}

module.exports = {
  parsePlaylist,
  sortByTitleAndUrl,
  appendToFile,
  createFile,
  readFile,
  loadEPG,
  createChannel,
  getBasename,
  addToCache,
  checkCache,
  clearCache,
  validateUrl,
  skipPlaylist,
  supportedCategories
}