package com.jcumulus.server.rtmfp.application;

/**
 * jCumulus is a Java port of Cumulus OpenRTMP
 *
 * Copyright 2011 OpenRTMFP
 *
 * This program 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.
 *
 * This program 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 received along this program for more
 * details (or else see http://www.gnu.org/licenses/).
 *
 *
 * This file is a part of jCumulus.
 */

import com.jcumulus.server.rtmfp.P;
import com.jcumulus.server.rtmfp.packet.*;
import com.jcumulus.server.rtmfp.publisher.E;
import com.jcumulus.server.rtmfp.publisher.FlowWriter;
import com.google.common.base.Strings;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.locks.LockSupport;
import org.apache.log4j.Logger;

// Referenced classes of package com.jcumulus.server.rtmfp.application:
//            G, I

public class Publication
{
    private static final Logger F = Logger.getLogger(Publication.class);
    private P E;
    private FlowWriter A;
    private boolean G;
    private String J;
    private int I;
    private Map H;
    private G D;
    private G C;
    private _A B;


    private class _A extends Thread
    {

        public void A()
        {
            D = true;
            interrupt();
        }

        public void run()
        {
            long l = System.nanoTime();
            do
            {
                if(D)
                    break;
                l += G * 0xf4240L;
                A.D();
                long l1 = l - System.nanoTime() - E * 0xf4240L;
                if(l1 > 0L)
                    LockSupport.parkNanos(l1);
            } while(true);
        }

        protected static final long C = 0L;
        protected static final long B = 0xf4240L;
        protected long E;
        protected Object F;
        protected long G;
        protected volatile boolean D;
        final Publication A;

        private _A()
        {
            super();
            A = Publication.this;

            F = new Object();
            G = 20L;
            D = false;
            setPriority(10);

            E = 0L; // timing_shift
        }

    }


    public Publication(String s)
    {
        G = false;
        H = new HashMap();
        D = new G();
        C = new G();
        B = new _A();
        B.start();
        F.info((new StringBuilder()).append("New publication ").append(s).toString());
        J = s;
    }

    public boolean A(P p, int i, FlowWriter h, boolean flag)
    {
        Listener j = (Listener)H.get(Integer.valueOf(i));
        if(j != null)
        {
            F.warn((new StringBuilder()).append("Listener ").append(i).append(" is already subscribed for publication ").append(I).toString());
            return true;
        }
        Listener k = new Listener(i, this, h, flag);
        if(p.B(k))
        {
            H.put(Integer.valueOf(i), k);
            h.A("Play.Reset", (new StringBuilder()).append("Playing and resetting ").append(J).toString());
            h.A("Play.Start", (new StringBuilder()).append("Started playing ").append(J).toString());
            k.C();
            return true;
        } else
        {
            h.A("Play.Failed", (new StringBuilder()).append("Not authorized to play ").append(J).toString());
            return false;
        }
    }

    public void A(P p, int i)
    {
        Listener j = (Listener)H.get(Integer.valueOf(i));
        if(j == null)
        {
            F.warn((new StringBuilder()).append("Listener ").append(i).append(" is already unsubscribed of publication ").append(I).toString());
            return;
        } else
        {
            p.A(j);
            H.remove(Integer.valueOf(i));
            return;
        }
    }

    public void A(String s, String s1)
    {
        if(I == 0)
        {
            F.error((new StringBuilder()).append("Publication ").append(J).append(" is not published").toString());
            return;
        }
        if(A != null)
        {
            if(!Strings.isNullOrEmpty(s))
                A.A(s, s1);
            A.A("close");
        } else
        {
            F.warn((new StringBuilder()).append("Publisher ").append(I).append(" has no controller to close it").toString());
        }
    }

    public void A(P p, int i, FlowWriter h)
        throws Exception
    {
        if(I != 0)
        {
            if(h != null)
                h.A("Publish.BadName", (new StringBuilder()).append(J).append(" is already published").toString());
            throw new Exception((new StringBuilder()).append(J).append(" is already published").toString());
        }
        I = i;
        if(!p.A(this))
        {
            String s = (new StringBuilder()).append("Not allowed to publish ").append(J).toString();
            I = 0;
            if(h != null)
                h.A("Publish.BadName", s);
            throw new Exception(s);
        }
        E = p;
        A = h;
        G = false;
        Listener j;
        for(Iterator iterator = H.values().iterator(); iterator.hasNext(); j.A(J))
            j = (Listener)iterator.next();

        D();
        if(h != null)
            h.A("Publish.Start", (new StringBuilder()).append(J).append(" is now published").toString());
    }

    public void B(P p, int i)
    {
        B.A();
        if(I != i)
        {
            F.warn((new StringBuilder()).append("Unpublish '").append(J).append("' operation with a ").append(i).append(" id different than its publisher ").append(I).append(" id").toString());
            return;
        }
        Listener j;
        for(Iterator iterator = H.values().iterator(); iterator.hasNext(); j.B(J))
            j = (Listener)iterator.next();

        D();
        p.B(this);
        D.E();
        C.E();
        I = 0;
        E = null;
        A = null;
    }

    public void D()
    {
        Listener i;
        for(Iterator iterator = H.values().iterator(); iterator.hasNext(); i.B())
            i = (Listener)iterator.next();

    }

    public void A(String s, Packet a)
    {
        if(I == 0)
        {
            F.error((new StringBuilder()).append("Data packet pushed on a publication ").append(I).append(" who is idle").toString());
            return;
        }
        int i = a.H();
        for(Iterator iterator = H.values().iterator(); iterator.hasNext(); a.E(i))
        {
            Listener j = (Listener)iterator.next();
            j.A(s, a);
        }

        E.A(this, s, a);
    }

    public void B(int i, AudioPacket a, int j)
    {
        int k = a.H();
        if(j > 0)
            F.info((new StringBuilder()).append(j).append(" audio fragments lost on publication ").append(I).toString());
        C.A(i, a.M(), j);
        for(Iterator iterator = H.values().iterator(); iterator.hasNext(); a.E(k))
        {
            Listener l = (Listener)iterator.next();
            l.B(i, a);
        }

        if(E != null)
            E.B(this, i, a);
    }

    public void A(int i, AudioPacket a, int j)
    {
        if(j > 0)
            G = false;
        if((a.K() & 0xf0) == 16)
            G = true;
        D.A(i, a.M(), j);
        if(j > 0)
            F.info((new StringBuilder()).append(j).append(" video fragments lost on publication ").append(I).toString());
        if(!G)
        {
            F.debug((new StringBuilder()).append("No key frame available on publication ").append(I).append(", frame dropped to wait first key frame").toString());
            D.G();
            return;
        }
        int k = a.H();
        for(Iterator iterator = H.values().iterator(); iterator.hasNext(); a.E(k))
        {
            Listener l = (Listener)iterator.next();
            l.A(i, a);
        }

        if(E != null)
            E.A(this, i, a);
    }

    public int B()
    {
        return I;
    }

    public Map A()
    {
        return H;
    }

    public String C()
    {
        return J;
    }


}
