// Warning
// this is not very used anyway
//
// currently here is some stuf which need some rework
// to work with broken gcc3.0


#include "AsfNetworkInputStream.h"
#include "NetworkIterator.h"
#include "AsxReader.h"
#include "asf_guids.h"

#include <string>

#include <utils.h>
#include <except.h>

#include <string.h> // strerror

#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#include <stdio.h>
#include <iostream>

using namespace std;

/*	Here goes network code 		*/

#define __MODULE__ "AsfNetworkInputStream"

const char* ASFNetworkInputStream::m_pcFirstRequest =
    "GET %s HTTP/1.0\r\n"
    "Accept: */*\r\n"
    "User-Agent: NSPlayer/4.1.0.3856\r\n"
    "Host: %s\r\n"
    "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=0:0,request-context=1,max-duration=0\r\n"
    "Pragma: xClientGUID={c77e7400-738a-11d2-9add-0020af0a3278}\r\n"
    "Connection: Close\r\n\r\n";

const char* ASFNetworkInputStream::m_pcSeekableRequest =
    "GET %s HTTP/1.0\r\n"
    "Accept: */*\r\n"
    "User-Agent: NSPlayer/4.1.0.3856\r\n"
    "Host: %s\r\n"
    "Pragma: no-cache,rate=1.000000,stream-time=%u,stream-offset=%u:%u,request-context=2,max-duration=%u\r\n"
    "Pragma: xPlayStrm=1\r\n"
    "Pragma: xClientGUID={c77e7400-738a-11d2-9add-0020af0a3278}\r\n"
    "Pragma: stream-switch-count=1\r\n"
    "Pragma: stream-switch-entry=ffff:1:0\r\n"
    "Connection: Close\r\n\r\n";

const char* ASFNetworkInputStream::m_pcLiveRequest =
    "GET %s HTTP/1.0\r\n"
    "Accept: */*\r\n"
    "User-Agent: NSPlayer/4.1.0.3856\r\n"
    "Host: %s\r\n"
    "Pragma: no-cache,rate=1.000000,request-context=2\r\n"
    "Pragma: xPlayStrm=1\r\n"
    "Pragma: xClientGUID={c77e7400-738a-11d2-9add-0020af0a3278}\r\n"
    "Pragma: stream-switch-count=1\r\n"
    "Pragma: stream-switch-entry=ffff:1:0\r\n"
    "Connection: Close\r\n\r\n";

ASFNetworkInputStream::ASFNetworkInputStream(const char* pszFile)
{
    char *proxyenv;

    m_bQuit=false;
    //_interrupt=false;
    m_bHeadersValid=false;
    m_bFinished=false;
    m_bWaiting=false;
    //m_File=pszFile;
    m_Ctype=Unknown;
    m_iTime=0;
    m_pReader=0;
    m_Server=pszFile;
    m_iRedirectSize=4096;

    string::size_type start, end;
    start=m_Server.find("://");
    cout<<"Testing "<<m_Server<<endl;
    if(start==string::npos)
	throw FATAL("Not an URL");
    if(start+3>=m_Server.size())
	throw FATAL("Not an URL");
    start+=3;
    end=m_Server.find("/", start);
    if(end==string::npos)
	throw FATAL("Not an URL");
    m_Filename=m_Server.substr(end);
    m_Server=m_Server.substr(start, end-start);
    cout<<"Server "<<m_Server<<", filename "<<m_Filename<<endl;
// style hostname:port eg 192.168.10.10:8080
    proxyenv=getenv("HTTP_PROXY");
    if (proxyenv) {
      m_Proxyhost = proxyenv;
      end = m_Proxyhost.find(":");
      if (end==string::npos) m_Proxyport=80;
      else m_Proxyport = m_Proxyhost.substr(end+1,string::npos);
      m_Proxyhost = m_Proxyhost.substr(0,end);
      m_Filename = m_File;
     cout<<"Proxy host "<<m_Proxyhost<<" port "<<m_Proxyport<<" file "<<m_Filename<<endl;
    }
    m_pcReadbuffer=(char *) malloc(65536+8);
    pipe(m_piPipe);
    pthread_mutex_init(&m_Mutex, 0);
    pthread_cond_init(&m_Cond, 0);
    pthread_create(&m_Thread, 0, &ASFNetworkInputStream::threadStarter, this);
}

ASFNetworkInputStream::~ASFNetworkInputStream()
{
    m_bQuit=true;
    interrupt();
    pthread_join(m_Thread, 0);
    list<packet*>::iterator it;
    for (it = m_Cache.begin(); it!=m_Cache.end(); it++)
	delete *it;
    list<NetworkIterator*>::iterator it2;
    for (it2 = m_Iterators.begin(); it2!=m_Iterators.end(); it2++)
	(*it2)->releaseInternal();
    close(m_piPipe[0]);
    close(m_piPipe[1]);
    pthread_mutex_destroy(&m_Mutex);
//    pthread_mutex_destroy(&_cond_mutex);
    pthread_cond_destroy(&m_Cond);
    free(m_pcReadbuffer);
    delete m_pReader;
}

void ASFNetworkInputStream::flushPipe()
{
    int pipe_flags=fcntl(m_piPipe[0], F_GETFL);
    char c;
    fcntl(m_piPipe[0], F_SETFL, pipe_flags | O_NONBLOCK);
    while(::read(m_piPipe[0], &c, 1)>0);
    fcntl(m_piPipe[0], F_SETFL, pipe_flags & ~O_NONBLOCK);
}

/* time in milliseconds */
void ASFNetworkInputStream::seekInternal(int seektime, NetworkIterator* requester)
{
    m_bFinished=false;
//    if(requester->_valid)
//    {
	interrupt();
	while(!m_bWaiting)
	    avi_usleep(10000);
	clear();
	for (list<NetworkIterator*>::iterator it = m_Iterators.begin();
	    it!=m_Iterators.end(); it++)
	{
//	    if(*it==requester)continue;
//	    (*it)->_valid=false;
	    (*it)->m_UndoIt.clear();
	    (*it)->m_It=m_Cache.end();
	}
	m_iTime=seektime;
	interrupt();
//	requester->_undo_it.clear();
//	requester->_it=_cache.end();
//    }
//    else
//    {
//	requester->_undo_it.clear(); //just in case
//	requester->_it=_cache.end();
//	requester->_valid=true;
//    }
}

void ASFNetworkInputStream::unregister(NetworkIterator* requester)
{
    m_Iterators.remove(requester);
}

void* ASFNetworkInputStream::threadStarter(void* arg)
{
    ASFNetworkInputStream* _this=(ASFNetworkInputStream*)arg;
    return _this->threadFunc();
}


const MainASFHeader& ASFNetworkInputStream::getHeader() const
{
    pthread_mutex_lock(&m_Mutex);
    if (!m_bHeadersValid)
    	pthread_cond_wait(&m_Cond, &m_Mutex);
    pthread_mutex_unlock(&m_Mutex);
    return m_Header;
}
const vector<ASFStreamHeader>& ASFNetworkInputStream::getStreams() const
{
    pthread_mutex_lock(&m_Mutex);
    if(!m_bHeadersValid)
    	pthread_cond_wait(&m_Cond, &m_Mutex);
    pthread_mutex_unlock(&m_Mutex);
    return m_Streams;
}
void ASFNetworkInputStream::interrupt()
{
    Debug cout<<"ASFNetworkInputStream::Interrupt()"<<endl;
    pthread_cond_broadcast(&m_Cond);
//    _interrupt=true;
    char c;
    write(m_piPipe[1], &c, 1);
}
double ASFNetworkInputStream::cacheSize() const
{
    Debug cout << "ASFNetworkInputStream::cacheSize() " << m_Cache.size() << "   finished " << m_bFinished << endl;
    if(m_bFinished)return 1.;
    double s=m_Cache.size()/50.;
//    cout<<s<<endl;
    return s;
}

void ASFNetworkInputStream::clear()
{
    Debug cout<<"ASFNetworkInputStream::clear()"<<endl;
    pthread_mutex_lock(&m_Mutex);
    for (list<packet*>::iterator it=m_Cache.begin(); it!=m_Cache.end(); it++)
	delete *it;
    m_Cache.clear();
    pthread_mutex_unlock(&m_Mutex);
}

void* ASFNetworkInputStream::threadFunc()
{
/**
 This func tries to open URL m_File. If it succeeds,
 it loads its headers, puts true to m_bHeadersValid
 and starts caching packets. If it fails, it puts
 true to m_bHeadersValid and to m_bQuit, and then exits.
**/
    int fd=-1;
    char hostname[256];
    char* pcRequest=0;
    int seekable_length=strlen(m_pcSeekableRequest) + m_Filename.size() + 2 * 256;
    int live_length=strlen(m_pcLiveRequest)+m_Filename.size()+ 2 * 256;
    fd=createSocket();
    if(fd<0)goto terminated;
    if(gethostname(hostname, 256))
    {
	cout<<"WARNING: gethostname() failed ( "<<strerror(errno)<<" )"<<endl;
	hostname[255]=0;
    }
    cout<<"Socket created"<<endl;
    pcRequest=new char[strlen(m_pcFirstRequest) + m_Filename.size() + 2 * 256];
    sprintf(pcRequest, m_pcFirstRequest, m_Filename.c_str(), m_Server.c_str());
    if(!checkContent(fd, pcRequest, m_Ctype))
    {
	cout<<"Check_content() aborted"<<endl;
	goto terminated;
    };
    cout<<"Check_content() successful"<<endl;
    delete[] pcRequest;
    if(seekable_length>live_length)live_length=seekable_length;
    pcRequest=new char[live_length];
    switch(m_Ctype)
    {
    case Unknown:
	cout<<"Unknown content type"<<endl;
        close(fd);
	fd=-1;
	goto terminated;
    case Redirect:
	cout<<"Redirector"<<endl;
	readRedirect(fd);
        close(fd);
	fd=-1;
	goto terminated;
    default:
        break;
    }
    close(fd);
    fd=-1;
    cout<<"content acceptable"<<endl;
    while(1)
    {
	char c;
	fd=createSocket();
	if(fd<0)goto terminated;
	cout<<"Socket created"<<endl;
	switch(m_Ctype)
	{
	case Prerecorded:
	    sprintf(pcRequest, m_pcSeekableRequest, m_Filename.c_str(), m_Server.c_str(),
		m_iTime, 0, 0, 0x7FFFFFFF);
	    break;
	case Live:
	    sprintf(pcRequest, m_pcLiveRequest, m_Filename.c_str(),  m_Server.c_str());
	    break;
	default:
	    cout << "AsfNetworkInputStream::threadFunc() unhandled case " << m_Ctype << endl;
	}
        if(!checkContent(fd, pcRequest, m_Ctype))
	{
	    cout<<"check_content() aborted"<<endl;
	}
	cout<<"check_content() nr 2 successful"<<endl;
	if(m_bQuit)
	    goto terminated;
	m_bFinished=false;
	if(!readContent(fd))
	{
	    cout<<"read_content() aborted"<<endl;
	}
	else
	    cout<<"read_content() successful"<<endl;
	if(m_bQuit)
	    goto terminated;
	close(fd);
	cout<<"Waiting for wake up"<<endl;
	m_bWaiting=true;
	::read(m_piPipe[0], &c, 1);
	flushPipe();
	cout<<"Continuing..."<<endl;
	m_bWaiting=false;
    };
    delete[] pcRequest;
    pcRequest=0;
    close(fd);
    return 0;
terminated:
    m_bHeadersValid=true;
    m_bQuit=true;
    pthread_cond_broadcast(&m_Cond);
    delete[] pcRequest;
    if (fd>=0)
	close(fd);
    return (void*)-1;
}

bool ASFNetworkInputStream::isValid()
{
    if(!m_bHeadersValid)return false;
    if(m_pReader)return true;
    if(m_bQuit)return false;
    return true;
}

int ASFNetworkInputStream::createSocket(int port)
{
    int fd=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (fd < 0)
    {
	cout<<"socket() failed ( "<<strerror(errno)<<" )???"<<endl;
	return -1;
    }

    sockaddr_in sa;
    int proxyport;
    hostent* he;

    sa.sin_family=AF_INET;
    proxyport = atoi(m_Proxyport.c_str());
    if (proxyport != 0)
    {
	sa.sin_port = htons(proxyport);
	he = gethostbyname(m_Proxyhost.c_str());
    }
    else
    {
	sa.sin_port = htons(port);
	he = gethostbyname(m_Server.c_str());
    }
//    if(!inet_aton(m_Server.c_str(), &sa.sin_addr))
    if(!he || !he->h_addr_list[0])
    {
	cout<<"WARNING: could not resolve server name ( "<<strerror(h_errno)<<" )"<<endl;
    }
    sa.sin_addr=*(in_addr*)(he->h_addr_list[0]);
    if(connect(fd, (sockaddr*)&sa, sizeof(sa)))
    {
	cout<<"WARNING: connection failed ( "<<strerror(errno)<<" )"<<endl;
	close(fd);
	return -1;
    }
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
    return fd;
}

int ASFNetworkInputStream::read(int fd, void* buffer, int size)
{
    static int lm_iFd=-1;
    if (lm_iFd<0)
	lm_iFd=open("./log", O_WRONLY|O_CREAT|O_TRUNC, 00777);
    int result=0;
    while(1)
    {
//	if(_interrupt)throw FATAL("Interrupted");
	if(m_bQuit)throw FATAL("Quitting");

	fd_set fset;
	FD_ZERO(&fset);
	FD_SET(fd, &fset);
	struct timeval tout = {2,0};

	if (select(fd + 1, &fset, 0, 0, &tout) == 0)
	{
            cout << "TIMEOUTED " << endl;
	    pthread_mutex_lock(&m_Mutex);
	    pthread_cond_broadcast(&m_Cond);
	    pthread_mutex_unlock(&m_Mutex);
            continue;
	}

	int tmp=::read(fd, buffer, size-result);
//	if((size>32)&&(tmp>0))cout<<tmp<<" <= "<<size-result<<endl;
//	if(errno!=EAGAIN)
//	    cout<<"Read() returns "<<tmp<<", error "<<errno<<endl;
	if(tmp<=0)
	{
	    if(errno!=EAGAIN && errno!=EINTR)
	    {
		cout<<"Aborting: read() returned "<<errno<<"( "<<strerror(errno)<<" )"<<endl;
		if(result>0)return result;
		throw FATAL("read() failed");
	    }

	    fd_set fds;
	    FD_ZERO(&fds);
    	    FD_SET(fd, &fds);
    	    FD_SET(m_piPipe[0], &fds);
	    int mx=(fd>m_piPipe[0])?fd:m_piPipe[0];
//	    cout<<"Entering select()"<<endl;
	    int err=select(mx+1, &fds, 0, 0, 0);
	    if(err<0)
	    {
		cout<<"select() returns nonzero"<<endl;
		continue;
	    }
	    if(FD_ISSET(m_piPipe[0], &fds))
	    {
//		char c;
		flushPipe();
//	        ::read(m_piPipe[0], &c, 1);
		throw FATAL("Interrupted");
	    }
	    if(FD_ISSET(fd, &fds))
	    {
//		cout<<"fd is set"<<endl;
	        tmp=::read(fd, buffer, size-result);
//		if(size>32)cout<<tmp<<" <= "<<size-result<<endl;
//		cout<<"Read "<<tmp<<" bytes"<<endl;
	    }
        }
        result+=tmp;
        buffer=(char*)buffer+tmp;
	if(result>=size)
	{
		if(lm_iFd>=0)
		{
		    Debug if(result>32)cout<<"stream::read(): returned "<<result<<" of "<<size<<" bytes"<<endl;
		    write(lm_iFd, (char*)buffer-result, result);
		    fsync(lm_iFd);
		}
		return result;
	}
    }
    return 0;
}

bool ASFNetworkInputStream::readContent(int fd)
{
    char *buffer;
    buffer=m_pcReadbuffer;
    list<packet*>::iterator old_end;
    int lfd=-1;
    char tmpfilename[]="/tmp/asfXXXXXX";
    if (getenv("WRITE_ASF"))
	lfd=mkstemp(tmpfilename);
    try
    {
    for (;;)
    {
	struct
	{
    	    unsigned short kind;
	    unsigned short size;
    	    unsigned int seq;
	    unsigned short tmp;
	    unsigned short size_confirm;
	} chhdr;
	if (cacheSize()>20.)
	{
	    avi_usleep(1000000);
	    char c;
	    // how to do this more properly?
	    int pipe_flags=fcntl(m_piPipe[0], F_GETFL);
	    fcntl(m_piPipe[0], F_SETFL, pipe_flags | O_NONBLOCK);
	    if(::read(m_piPipe[0], &c, 1)>0)
	    {
		fcntl(m_piPipe[0], F_SETFL, pipe_flags & ~O_NONBLOCK);
		cout<<"read_content(): interrupted"<<endl;
		return false;
	    }
	    fcntl(m_piPipe[0], F_SETFL, pipe_flags & ~O_NONBLOCK);
	    continue;
	}

        read(fd, &chhdr, sizeof(chhdr));
	Debug cout<<hex<<chhdr.kind<<" "<<chhdr.size<<" "<<chhdr.seq
	<<" "<<chhdr.tmp<<" "<<chhdr.size_confirm<<dec<<endl;
	if(chhdr.size<8)
	{
	    cout<<"I don't like chunk size"<<endl;
	    return false;
	}
	if(chhdr.size!=chhdr.size_confirm)
	{
	    cout<<"size!=size_confirm("<<chhdr.size<<"!="<<chhdr.size_confirm<<")"<<endl;
	    return false;
	}
	unsigned short& size=chhdr.size;
	size-=8;
	char* ptr=buffer;
	packet* p;
	ASFStreamHeader strhdr;
        switch(chhdr.kind)
        {
	case 0x4424: //data
	    if(!m_bHeadersValid)
	    {
		cout<<"Unexpected data chunk"<<endl;
		return false;
	    }
	    if(size>m_Header.pktsize)
	    {
		cout<<"WARNING: size>m_Header.pktsize"<<endl;
		return false;
	    }
	    Debug cout<<"Creating packet with size "<<m_Header.pktsize<<endl;
	    p=new packet(m_Header.pktsize);
	    Debug cout<<"Created packet "<<p<<endl;
	    read(fd, &(*p)[0], size);
	    if(lfd>=0){write(lfd, &(*p)[0], m_Header.pktsize);fsync(lfd);}
	    Debug cout<<"Read "<<size<<" bytes"<<endl;
	    pthread_mutex_lock(&m_Mutex);
	    old_end=m_Cache.end();
	    m_Cache.push_back(p);
//	    cout<<"New cache size: "<<m_Cache.size()<<endl;
	    for(list<NetworkIterator*>::iterator it2=m_Iterators.begin();
		it2!=m_Iterators.end(); it2++)
		if((*it2)->m_It==old_end)/* a bit dirty */
		{
		    if(!(*it2)->m_bValid)continue;
		    Debug cout<<"Found old iterator"<<endl;
		    (*it2)->m_It=m_Cache.end();
		    (*it2)->m_It--;
		    Debug cout<<"Now it points to "<<*((*it2)->m_It)<<endl;
		}
	    pthread_cond_broadcast(&m_Cond);
	    pthread_mutex_unlock(&m_Mutex);
	    break;
	case 0x4524:
	{
	    m_bFinished=true;
	    pthread_cond_broadcast(&m_Cond);
	    cout<<"read_content(): finished transmission"<<endl;
	    return true;
	}
	case 0x4824:
	    read(fd, ptr, size);
	    if(lfd>=0){write(lfd, ptr, size);fsync(lfd);}
	    while(size>24)
	    {
		GUID guid;
		int64_t ch_size;
		guid=*(GUID*)ptr;ptr+=16;
		ch_size=*(int64_t*)ptr; ptr+=8;
		cout<<"ch_size "<<(double)ch_size<<", size "<<(double)size<<endl;
		if(ch_size>size)ch_size=size;
		if(ch_size<24)break;
		ch_size-=24;
		switch(LookupChunkType(guid))
		{
		case 0: //unknown chunk type
    		default:
		    cout<<"Unknown guid"<<endl;
		    size-=ch_size+24;
		    ptr+=ch_size;
	    	    continue;
		case 6:
		    cout<<"Main ASF header"<<endl;
		    if(ch_size<sizeof(MainASFHeader))
			throw FATAL("Wrong ASF header size");
		    if(ch_size>sizeof(MainASFHeader))
			cout<<"Warning: unexpected size of ASF header "<<size<<endl;
		    m_Header=*(MainASFHeader*)ptr;
		    size-=ch_size+24;
		    ptr+=ch_size;
		    continue;
		case 4:
		    cout<<"ASF stream header"<<endl;
		    if(ch_size>sizeof(strhdr))
		    cout<<"Warning: unexpected size of ASF stream header "<<size<<endl;
		    memcpy(&strhdr, ptr, (ch_size<sizeof(strhdr))?ch_size:sizeof(strhdr));
		    m_Streams.push_back(strhdr);
		    size-=ch_size+24;
		    ptr+=ch_size;
		    continue;
		case 1:
		    cout<<"ASF file header"<<endl;
		    size-=6+24;
		    ptr+=6;
		    continue;
		case 2:
		    cout<<"ASF data chunk"<<endl;
		    size-=26+24;
		    ptr+=26;
		    continue;
		}
	    }
	    pthread_mutex_lock(&m_Mutex);
	    if(!m_bHeadersValid)pthread_cond_broadcast(&m_Cond);
	    cout<<"Received valid headers\n";
	    m_bHeadersValid=true;
	    pthread_mutex_unlock(&m_Mutex);
	    break;
        }//switch() on 4424/4524/4824
	if(m_bHeadersValid)
	{
	    recheck();
	    Debug cout<<"recheck() complete"<<endl;
	}
    }
    }
    catch(FatalError e)
    {
    	cout<<"read_content(): caught an exception"<<endl;
	e.Print();
	if(lfd>=0)close(lfd);
	return false;
    }
}

void ASFNetworkInputStream::recheck()
{
    pthread_mutex_lock(&m_Mutex);
    while(m_Cache.size())
    {
	Debug cout<<"Cache size "<<m_Cache.size()<<endl;
	list<packet*>::iterator it=m_Cache.begin();
	bool reached_flag=false;
	for(list<NetworkIterator*>::iterator it2=m_Iterators.begin();
	    it2!=m_Iterators.end(); it2++)
	{
	    NetworkIterator* ni=*it2;
//	    if(!ni->m_bValid)continue;
	    for(unsigned z=0; z<ni->m_UndoIt.size(); z++)
	    if(ni->m_UndoIt[z]==it)
	    {
		reached_flag=true;
		break;
	    }
	    if(reached_flag)break;
	    if(ni->m_It==it)
	    {
		reached_flag=true;
		break;
	    }
	}
	if(reached_flag)break;
	Debug cout<<"Erasing packet"<<endl;
	delete *it;
	m_Cache.pop_front();
    }
    pthread_mutex_unlock(&m_Mutex);
}

bool ASFNetworkInputStream::checkContent(int fd, const char* request, Content& ctype)
{
    int eol;
    int hdrpos;
    int linepos;
    int linenum;
    char HTTPHeader[1024];
    //int resume = 0;

    char HTTPLine[512];
    char HTTPMessage[128];
    char *hdrptr;
    int errorcode;

    char Features[256];
    char ContentType[256];
//    cout<<"Entered check_content()"<<endl;
    write(fd, request, strlen(request));
//
    cout<<"Sent data:"<<endl<<request<<endl;
    errorcode = 0;
    strcpy(HTTPMessage, "<none>");

    strcpy(ContentType, "");
    strcpy(Features, "");

    eol = 0;
    hdrpos = 0;
    linepos = 0;
    linenum = 0;
    try
    {
	for (;;)
	{
    	    char c;
    	    read(fd, &c, 1);

	    Debug cout<<c<<flush;
    	    if ((c != '\r') && (c != '\n'))
            {
                eol = 0;
                HTTPLine[linepos++] = c;
            }
            else
                HTTPLine[linepos++] = 0;

            if (c == '\n')
            {
                if (eol == 0)
                {
        	    linepos = 0;
                    eol = 1;
                    linenum++;
            	    hdrptr = HTTPLine;

                    /* Parse first line of HTTP reply */
                    if (linenum == 1)
                    {
                        if ((!strncasecmp(hdrptr, "HTTP/1.0 ", 9)) ||
                            (!strncasecmp(hdrptr, "HTTP/1.1 ", 9))   )
                        {
                            hdrptr+=9;
                            sscanf(hdrptr, "%d", &errorcode);
                            hdrptr+=4;
                            strcpy(HTTPMessage, hdrptr);
                        }
                        else
                        {
                            cout<<"Illegal server reply! Expected HTTP/1.0 or HTTP/1.1"<<endl;
                            ctype=Unknown;
                        }
                    }
                    else
                    {
                        /* Parse all other lines of HTTP reply */
                        if (!strncasecmp(hdrptr, "Content-Type: ", 14))
                	{
                    	    hdrptr+=14;
                            strncpy(ContentType, hdrptr, sizeof(ContentType));
                        }
                        if (!strncasecmp(hdrptr, "Content-Length: ", 16))
                	{
                    	    hdrptr+=16;
			    m_iRedirectSize=atoi(hdrptr);
                        }

                        /* Parse all other lines of HTTP reply */
                        if (!strncasecmp(hdrptr, "Pragma: ", 8))
                        {
                            hdrptr+=8;
                            if (!strncasecmp(hdrptr, "features=", 9))
                            {
                                hdrptr+=9;
                                strncpy(Features, hdrptr, sizeof(Features));
                            }
                        }
                    }
                }
                else
                {
                    HTTPHeader[hdrpos++] = 0;
                    break;
                }
            }
            HTTPHeader[hdrpos++] = c;
//        else
//        {
//            if (eos()) break;
//        }
	}
    }
    catch(FatalError& e)
    {
	e.Print();
        ctype=Unknown;
	return false;
    }
    cout<<HTTPHeader<<endl;
    ctype=Unknown;

    /* Determine whether this is live content or not */
    if (!strcasecmp(ContentType, "application/octet-stream"))
    {
        if (strstr(Features, "broadcast"))
        {
            ctype=Live;
        }
        else
        {
            ctype=Prerecorded;
        }
    }
    else
    {
        if ((!strcasecmp(ContentType, "audio/x-ms-wax")) ||
            (!strcasecmp(ContentType, "audio/x-ms-wma")) ||
            (!strcasecmp(ContentType, "video/x-ms-asf")) ||
            (!strcasecmp(ContentType, "video/x-ms-afs")) ||
            (!strcasecmp(ContentType, "video/x-ms-wvx")) ||
            (!strcasecmp(ContentType, "video/x-ms-wmv")) ||
            (!strcasecmp(ContentType, "video/x-ms-wma"))   )
        {
            ctype=Redirect;
        }
        else
        {
            cout<<"unknown content-type: "<<ContentType<<endl;
        }
    }
    return true;
}

bool ASFNetworkInputStream::readRedirect(int fd)
{
    int readsize=0;
    vector<char> buffer(m_iRedirectSize);
    try
    {
	for(int i=0; i<m_iRedirectSize; i++)
	{
	    read(fd, &buffer[i], 1);
	    readsize=i;
	}
    }
    catch(...)
    {
    }
    m_pReader=new ASX_Reader;
    if(!m_pReader->create(buffer))
    {
	delete m_pReader;
	m_pReader=0;
    }
    return false;//????? FIXME
}

bool ASFNetworkInputStream::getURLs(vector<string>& urls)
{
    return (m_pReader) ? m_pReader->getURLs(urls) : false;
}

Iterator* ASFNetworkInputStream::getIterator(int id)
{
    m_Iterators.push_back(new NetworkIterator(this));
    Iterator* it=m_Iterators.back();
    it->addRef();
    return it;
}
