########################
###   myapp.finders.py
########################

from django.contrib.staticfiles.finders import BaseStorageFinder
from django.contrib.staticfiles.finders import get_finders
from django.core.files.storage import FileSystemStorage
from contextlib import contextmanager
from django.core.files import File
from django.conf import settings
from itertools import takewhile
import subprocess
import tempfile
import operator
import shlex
import os

class CoffeeStorage(FileSystemStorage):
    def path(self, path):
        """Get path to the actual coffeescript file"""
        coffee_path = "{}.coffee".format(path[:-3])
        return os.path.join(self.location, coffee_path)

    def compile(self, src):
        """Let's compile some coffeescript!"""
        cmd = 'coffee -c -p {}'.format(src)

        proc = subprocess.Popen(shlex.split(cmd)
            , stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE
            )
        out, err = proc.communicate()

        if proc.returncode != 0 or err:
            raise Exception('%s\n\n%s' % (err, out))
        else:
            return out

    @contextmanager
    def open(self, path):
        """Open the coffeescript file as a javascript file"""
        with tempfile.NamedTemporaryFile() as f:
            f.write(self.compile(self.path(path)))
            yield File(f)

class CoffeescriptFinder(BaseStorageFinder):
    storage = CoffeeStorage

    def other_finders(self):
        """Get all the finders that come before this one"""
        not_self = lambda finder : operator.is_not(self, finder)
        return takewhile(not_self, get_finders())

    def list(self, ignore_patterns):
        """Find all the coffeescript files and say they have js equivalents"""
        for finder in self.other_finders():
            for path, _ in finder.list(ignore_patterns):
                if path.endswith(".coffee"):
                    js_path = "{}.js".format(path[:-7])
                    location = finder.find(path)[:-len(path)]
                    yield js_path, CoffeeStorage(location=location)

    def find(self, path, all=False):
        """Find the coffeescript file"""
        if path.endswith(".js"):
            coffee_path = "{}.coffee".format(path[:-3])
            for finder in self.other_finders():
                found = finder.find(coffee_path)
                if found:
                    storage = CoffeeStorage(location=found[:-len(path)])
                    destination = os.path.join(settings.CACHE_DIR, path)
                    if not os.path.exists(destination):
                        os.makedirs(os.path.dirname(destination))

                    with open(destination, 'w') as dest:
                        dest.write(storage.compile(found))

                    return destination

        # Nothing found
        return all and None or []

########################
###   settings.py
########################

CACHE_DIR = "some/path/to/where/coffeescript/is/compiled/for/development"

# Make sure coffeescript finder is after other finders
# It uses these to actually find the coffeescript files to compile
STATICFILES_FINDERS = (
      'django.contrib.staticfiles.finders.FileSystemFinder'
    , 'django.contrib.staticfiles.finders.AppDirectoriesFinder'
    , 'myapp.finders.CoffeescriptFinder'
    )

# And serving assets in development
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
urlpatterns += staticfiles_urlpatterns()