# -*- coding: utf-8 -*-
# ------------------------------------------------------------
# pelisalacarta 4
# Copyright 2015 tvalacarta@gmail.com
# http://blog.tvalacarta.info/plugin-xbmc/pelisalacarta/
#
# Distributed under the terms of GNU General Public License v3 (GPLv3)
# http://www.gnu.org/licenses/gpl-3.0.html
# ------------------------------------------------------------
# This file is part of pelisalacarta 4.
#
# pelisalacarta 4 is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pelisalacarta 4 is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pelisalacarta 4.  If not, see <http://www.gnu.org/licenses/>.
# ------------------------------------------------------------
from threading import Lock, Event

class Cursor(object):
    def __init__(self, file):
        self._file = file
        self.pos = 0
        self.timeout = 30
        self.cache_size = self._file._client.buffer_size
        self.cache = {}
        self.lock = Lock()
        self.event = Event()
        self.cache_first = 0

    def fill_cache(self, first):
        self.cache_first = first

        with self.lock:
            for p in sorted(self.cache):
                if p < first: del self.cache[p]

        self.event.clear()
        for i in xrange(first, first + self.cache_size):
            if i <= self._file.last_piece:
                self._file._client.prioritize_piece(i, i-first)

    def has_piece(self,n):
        with self.lock:
            return n in self.cache

    def _wait_piece(self,pc_no):
        while not self.has_piece(pc_no):
            self.fill_cache(pc_no)
            self.event.wait(self.timeout)

    def _get_piece(self,n):
        with self.lock:
            if not n in self.cache:
                raise ValueError('index of of scope of current cache')
            return self.cache[n]

    def get_piece(self,n):
        self._wait_piece(n)
        return self._get_piece(n)

    def close(self):
        self._file.cursor = None

    def read(self, size=None):
        data = ""
        max_size=self._file.size - self.pos
        if not size:
          size = max_size
        else:
          size = min(size, max_size)

        if size:
            pc_no,ofs=self._file.map_piece(self.pos)
            data=self.get_piece(pc_no)[ofs: ofs+size]

            if len(data) < size:
                remains = size - len(data)
                pc_no +=1
                self.fill_cache(pc_no)
                while remains and self.has_piece(pc_no):
                    sz=min(remains, self._file.piece_size)
                    data+=self.get_piece(pc_no)[:sz]
                    remains-=sz
                    if remains:
                        pc_no+=1
                    self.fill_cache(pc_no)

        self.pos+=len(data)

        return data

    def seek(self,n):
        if n>self._file.size:
            n=self._file.size
        elif n<0:
            raise ValueError('Seeking negative')
        self.pos=n

    def tell(self):
        return self.pos

    def update_piece(self,n,data):
        with self.lock:
            pcs = sorted(self.cache)
            if len(pcs)< self.cache_size:
                if len(pcs):
                  new = max(pcs)+1
                else:
                  new = self.cache_first
                if n == new:
                    self.cache[n]=data
                    if n == self.cache_first:
                        self.event.set()

    def __enter__(self):
        return self

    def __exit__(self,exc_type, exc_val, exc_tb):
        self.close()
