import os, commands, re, tempfile
from subprocess import Popen, PIPE, STDOUT

import buffer, default, dirutil, regex, util, window
from point import Point
from method import Method, Argument, arg

class OpenFile(Method):
    '''Open file in a new buffer, or go to file's open buffer'''
    args = [arg('filename', dt="path", p="Open File: ", dv=default.path_dirname,
                ld=True, h="file to open")]
    def _execute(self, w, **vargs):
        b = w.application.open_path(vargs['filename'])
        SwitchBuffer().execute(w, buffername=b.name())
class OpenAesFile(Method):
    '''Open AES encrypted file in a new buffer, or go to file's open buffer'''
    args = [arg('filename', dt="path", p="Open AES File: ",
                dv=default.path_dirname, h="file to open"),
            arg('password', p="Use AES Password: ",
                h="the AES password the file was encrypted with")]
    def _execute(self, w, **vargs):
        b = w.application.open_path(vargs['filename'], 'aes', vargs['password'])
        SwitchBuffer().execute(w, buffername=b.name())
        return

class ViewBufferParent(Method):
    def _execute(self, w, **vargs):
        b = w.buffer
        if not hasattr(b, 'path'):
            w.set_error('Buffer has no path')
        elif b.path == '/':
            w.set_error("Root directory has no parent")
        else:
            path = os.path.dirname(b.path)
            w.application.methods['open-file'].execute(w, filename=path)

class SwitchBuffer(Method):
    '''Switch to a different'''
    args = [arg('buffername', dt="buffer", p="Switch To Buffer: ",
                dv=default.last_buffer, h="name of the buffer to switch to")]
    def _pre_execute(self, w, **vargs):
        a = w.application
        if len(a.bufferlist.buffers) < 1:
            raise Exception, "No other buffers"
    def _execute(self, w, **vargs):
        name = vargs['buffername']
        buf = None
        if w.application.has_buffer_name(name):
            b = w.application.bufferlist.get_buffer_by_name(name)
            w.application.switch_buffer(b)
        else:
            w.set_error("buffer %r was not found" % name)
class KillBuffer(Method):
    '''Close the current buffer'''
    force = False
    args  = [arg('buffername', dt="buffer", p="Kill Buffer: ",
                 dv=default.current_buffer, h="name of the buffer to kill")]
    def _execute(self, w, **vargs):
        name = vargs['buffername']
        a    = w.application
        if name not in a.bufferlist.buffer_names:
            raise Exception("Buffer %r does not exist" % name)
        elif len(a.bufferlist.buffers) == 1:
            raise Exception("Can't close only open buffer")
        self._to_kill    = a.bufferlist.buffer_names[name]
        self._old_window = w
        if self.force or not self._to_kill.changed():
            self._doit()
        else:
            self._prompt = "Buffer has unsaved changes; kill anyway? "
            a.open_mini_buffer(self._prompt, self._callback)
    def _doit(self):
        a = self._old_window.application
        b = self._to_kill
        a.close_buffer(b)
    def _callback(self, v):
        a = self._old_window.application
        if v == 'yes':
            self._doit()
            a.close_mini_buffer()
        elif v == 'no':
            a.close_mini_buffer()
        else:
            a.close_mini_buffer()
            a.set_error('Please type "yes" or "no"')

class ForceKillBuffer(KillBuffer):
    '''Close the current buffer, automatically discarding any changes'''
    force=True

class ListBuffers(Method):
    '''List all open buffers in a new buffer'''
    def _execute(self, w, **vargs):
        bl = w.application.bufferlist
        bnames = [b.name() for b in bl.buffers]
        bnames.sort()
        data = '\n'.join(bnames)
        w.application.data_buffer("*Buffers*", data, switch_to=True)
class SaveBufferAs(Method):
    '''Save the contents of a buffer to the specified path'''
    args = [arg('path', dt="path", p="Write file: ", ld=True,
                dv=default.current_working_dir, h="new path to use")]
    def _execute(self, w, **vargs):
        a = w.application
        curr_buffer = w.buffer
        curr_buffer_name = curr_buffer.name()
        data = curr_buffer.make_string()
        path = os.path.realpath(os.path.expanduser(vargs['path']))
        w.set_error("got %r (%d)" % (path, len(data)))
        if a.has_buffer_name(path):
            w.set_error("buffer for %r is already open" % path)
            return
        a.new_file_buffer(path, data, switch_to=True)
        a.methods['kill-buffer'].execute(w, buffername=curr_buffer_name)
        w.set_error('Wrote %r' % path)
class SaveBuffer(Method):
    '''Save the contents of a buffer'''    
    def _execute(self, w, **vargs):
        a = w.application
        if not w.buffer.changed():
            w.set_error("(No changes need to be saved)")
            return
        try:
            w.buffer.save()
            w.set_error("Wrote %s" % (w.buffer.path))
        except buffer.FileGoneError, e:
            w.buffer.save(force=True)
            w.set_error("File had disappeared! Wrote %s" % (w.buffer.path))
        except buffer.FileChangedError, e:
            self._old_window = w
            self._prompt = "File changed on-disk; reload(r), overwrite(o) or diff(d)? "
            a.open_mini_buffer(self._prompt, self._callback)
    def _callback(self, v):
        w = self._old_window
        a = w.application
        a.close_mini_buffer()
        if v in ('reload', 'r'):
            a.methods['reload-buffer'].execute(w)
        elif v in ('overwrite', 'o'):
            a.methods['overwrite-buffer'].execute(w)
        elif v in ('diff', 'd'):
            path1 = w.buffer.path
            tf = tempfile.NamedTemporaryFile(delete=False)
            tf.write(w.buffer.make_string())
            tf.close()
            path2 = tf.name
            a.methods['diff'].execute(w, path1=path1, path2=path2)
            os.unlink(path2)
            w.set_error("File changed on disk; showing differences")
        else:
            a.open_mini_buffer(self._prompt, self._callback)
            a.set_error('Please type reload(r), overwrite(o) or diff(d)')
        
class ReloadBuffer(Method):
    '''Reload the contents of a buffer from the filesystem'''
    def _execute(self, w, **vargs):
        w.buffer.reload()
        w.set_error("Buffer contents reloaded from %r" % w.buffer.path)

class OverwriteBuffer(Method):
    '''Unconditionally overwrite path with the contents of the buffer'''
    def _execute(self, w, **vargs):
        w.buffer.save(force=True)
        w.set_error("Wrote %s" % (w.buffer.path))

class ToggleTabs(Method):
    '''Toggle whether to write tabs out or not (defaults to false)'''
    def _execute(self, w, **vargs):
        b = w.buffer
        b.writetabs = not b.writetabs
        if b.writetabs:
            w.set_error("Buffer will translate %d spaces to a tab" % b.indentlvl)
        else:
            w.set_error("Buffer will not write tabs")

class Pwd(Method):
    '''Print the buffer's current working directory'''
    def _execute(self, w, **vargs):
        home = os.getenv('HOME')
        if hasattr(w.buffer, 'path'):
            cwd = os.path.dirname(w.buffer.path) + '/'
            if cwd.startswith(home):
                cwd = cwd.replace(home, '~')
            w.set_error("Directory %s" % cwd)
        else:
            w.set_error("Directory %s/" % os.getcwd())