import unittest
import datetime
from decimal import Decimal

from twisted.internet import defer

from scrapy.utils.serialize import SpiderReferencer, ScrapyJSONEncoder, ScrapyJSONDecoder
from scrapy.utils.py26 import json
from scrapy.spider import BaseSpider
from scrapy.http import Request, Response


class _EngineMock(object):
    def __init__(self, open_spiders):
        self.open_spiders = open_spiders

class CrawlerMock(object):
    def __init__(self, open_spiders):
        self.engine = _EngineMock(open_spiders)

class BaseTestCase(unittest.TestCase):

    def setUp(self):
        self.spider1 = BaseSpider('name1')
        self.spider2 = BaseSpider('name2')
        open_spiders = set([self.spider1, self.spider2])
        crawler = CrawlerMock(open_spiders)
        self.spref = SpiderReferencer(crawler)
        self.encoder = ScrapyJSONEncoder(spref=self.spref)
        self.decoder = ScrapyJSONDecoder(spref=self.spref)

class SpiderReferencerTestCase(BaseTestCase):

    def test_spiders_and_references(self):
        ref1 = self.spref.get_reference_from_spider(self.spider1)
        assert isinstance(ref1, str)
        assert self.spider1.name in ref1
        ref2 = self.spref.get_reference_from_spider(self.spider2)
        ref1_ = self.spref.get_reference_from_spider(self.spider1)
        assert ref1 == ref1_
        assert ref1 != ref2

        sp1 = self.spref.get_spider_from_reference(ref1)
        sp2 = self.spref.get_spider_from_reference(ref2)
        sp1_ = self.spref.get_spider_from_reference(ref1)
        assert isinstance(sp1, BaseSpider)
        assert sp1 is not sp2
        assert sp1 is sp1_

        # referring to spiders by name
        assert sp1 is self.spref.get_spider_from_reference('spider::name1')
        assert sp2 is self.spref.get_spider_from_reference('spider::name2')

        # must return string as-is if spider id not found
        assert 'lala' == self.spref.get_spider_from_reference('lala')
        # must raise RuntimeError if spider id is not found and spider is not running
        self.assertRaises(RuntimeError, self.spref.get_spider_from_reference, 'spider:fffffff')

    def test_encode_decode(self):
        sr = self.spref
        sp1 = self.spider1
        sp2 = self.spider2
        ref1 = sr.get_reference_from_spider(sp1)
        ref2 = sr.get_reference_from_spider(sp2)

        examples = [
            ('lala', 'lala'),
            (sp1, ref1),
            (['lala', sp1], ['lala', ref1]),
            ({'lala': sp1}, {'lala': ref1}),
            ({sp1: sp2}, {ref1: ref2}),
            ({sp1: {sp2: ['lala', sp1]}}, {ref1: {ref2: ['lala', ref1]}})
        ]
        for spiders, refs in examples:
            self.assertEqual(sr.encode_references(spiders), refs)
            self.assertEqual(sr.decode_references(refs), spiders)

class JsonEncoderTestCase(BaseTestCase):
    
    def test_encode_decode(self):
        sr = self.spref
        sp1 = self.spider1
        sp2 = self.spider2
        ref1 = sr.get_reference_from_spider(sp1)
        ref2 = sr.get_reference_from_spider(sp2)
        dt = datetime.datetime(2010, 1, 2, 10, 11, 12)
        dts = "2010-01-02 10:11:12"
        d = datetime.date(2010, 1, 2)
        ds = "2010-01-02"
        t = datetime.time(10, 11, 12)
        ts = "10:11:12"
        dec = Decimal("1000.12")
        decs = "1000.12"

        examples_encode_decode = [
            ('lala', 'lala'),
            (sp1, ref1),
            (['lala', sp1], ['lala', ref1]),
            ({'lala': sp1}, {'lala': ref1}),
            ({sp1: sp2}, {ref1: ref2}),
            ({sp1: {sp2: ['lala', sp1]}}, {ref1: {ref2: ['lala', ref1]}})
        ]
        for spiders, refs in examples_encode_decode:
            self.assertEqual(self.encoder.encode(spiders), json.dumps(refs))
            self.assertEqual(self.decoder.decode(json.dumps(refs)), spiders)

        examples_encode_only = [
            ({sp1: dt}, {ref1: dts}),
            ({sp1: d}, {ref1: ds}),
            ({sp1: t}, {ref1: ts}),
            ({sp1: dec}, {ref1: decs}),
        ]
        for spiders, refs in examples_encode_only:
            self.assertEqual(self.encoder.encode(spiders), json.dumps(refs))

        assert 'Deferred' in self.encoder.encode(defer.Deferred())

    def test_encode_request(self):
        r = Request("http://www.example.com/lala")
        rs = self.encoder.encode(r)
        assert r.method in rs
        assert r.url in rs

    def test_encode_response(self):
        r = Response("http://www.example.com/lala")
        rs = self.encoder.encode(r)
        assert r.url in rs
        assert str(r.status) in rs


if __name__ == "__main__":
    unittest.main()

