# All credits to https://github.com/LuanRT/googlevideo

from pytubezero.sabr.proto import BinaryWriter, BinaryReader


class ClientAbrState:

    @staticmethod
    def create_base_client_abr_state():
        return {
            "timeSinceLastManualFormatSelectionMs": 0,
            "lastManualDirection": 0,
            "lastManualSelectedResolution": 0,
            "detailedNetworkType": 0,
            "clientViewportWidth": 0,
            "clientViewportHeight": 0,
            "clientBitrateCapBytesPerSec": 0,
            "stickyResolution": 0,
            "clientViewportIsFlexible": False,
            "bandwidthEstimate": 0,
            "minAudioQuality": 0,
            "maxAudioQuality": 0,
            "videoQualitySetting": 0,
            "audioRoute": 0,
            "playerTimeMs": 0,
            "timeSinceLastSeek": 0,
            "dataSaverMode": False,
            "networkMeteredState": 0,
            "visibility": 0,
            "playbackRate": 0,
            "elapsedWallTimeMs": 0,
            "mediaCapabilities": bytearray(),
            "timeSinceLastActionMs": 0,
            "enabledTrackTypesBitfield": 0,
            "maxPacingRate": 0,
            "playerState": 0,
            "drcEnabled": False,
            "Jda": 0,
            "qw": 0,
            "Ky": 0,
            "sabrReportRequestCancellationInfo": 0,
            "l": False,
            "G7": 0,
            "preferVp9": False,
            "qj": 0,
            "Hx": 0,
            "isPrefetch": False,
            "sabrSupportQualityConstraints": 0,
            "sabrLicenseConstraint": bytearray(),
            "allowProximaLiveLatency": 0,
            "sabrForceProxima": 0,
            "Tqb": 0,
            "sabrForceMaxNetworkInterruptionDurationMs": 0,
            "audioTrackId": ""
        }

    @staticmethod
    def encode(message: dict, writer=None):
        if writer is None:
            writer = BinaryWriter()

        if message.get("timeSinceLastManualFormatSelectionMs", 0):
            writer.uint32(104).int64(message["timeSinceLastManualFormatSelectionMs"])
        if message.get("lastManualDirection", 0):
            writer.uint32(112).sint32(message["lastManualDirection"])
        if message.get("lastManualSelectedResolution", 0):
            writer.uint32(128).int32(message["lastManualSelectedResolution"])
        if message.get("detailedNetworkType", 0):
            writer.uint32(136).int32(message["detailedNetworkType"])
        if message.get("clientViewportWidth", 0):
            writer.uint32(144).int32(message["clientViewportWidth"])
        if message.get("clientViewportHeight", 0):
            writer.uint32(152).int32(message["clientViewportHeight"])
        if message.get("clientBitrateCapBytesPerSec", 0):
            writer.uint32(160).int64(message["clientBitrateCapBytesPerSec"])
        if message.get("stickyResolution", 0):
            writer.uint32(168).int32(message["stickyResolution"])
        if message.get("clientViewportIsFlexible", False):
            writer.uint32(176).bool(message["clientViewportIsFlexible"])
        if message.get("bandwidthEstimate", 0):
            writer.uint32(184).int64(message["bandwidthEstimate"])
        if message.get("minAudioQuality", 0):
            writer.uint32(192).int32(message["minAudioQuality"])
        if message.get("maxAudioQuality", 0):
            writer.uint32(200).int32(message["maxAudioQuality"])
        if message.get("videoQualitySetting", 0):
            writer.uint32(208).int32(message["videoQualitySetting"])
        if message.get("audioRoute", 0):
            writer.uint32(216).int32(message["audioRoute"])
        if message.get("playerTimeMs", 0):
            writer.uint32(224).int64(message["playerTimeMs"])
        if message.get("timeSinceLastSeek", 0):
            writer.uint32(232).int64(message["timeSinceLastSeek"])
        if message.get("dataSaverMode", False):
            writer.uint32(240).bool(message["dataSaverMode"])
        if message.get("networkMeteredState", 0):
            writer.uint32(256).int32(message["networkMeteredState"])
        if message.get("visibility", 0):
            writer.uint32(272).int32(message["visibility"])
        if message.get("playbackRate", 0):
            writer.uint32(285).float(message["playbackRate"])
        if message.get("elapsedWallTimeMs", 0):
            writer.uint32(288).int64(message["elapsedWallTimeMs"])
        if message.get("mediaCapabilities", b''):
            writer.uint32(306).bytes(message["mediaCapabilities"])
        if message.get("timeSinceLastActionMs", 0):
            writer.uint32(312).int64(message["timeSinceLastActionMs"])
        if message.get("enabledTrackTypesBitfield", 0):
            writer.uint32(320).int32(message["enabledTrackTypesBitfield"])
        if message.get("maxPacingRate", 0):
            writer.uint32(344).int32(message["maxPacingRate"])
        if message.get("playerState", 0):
            writer.uint32(352).int64(message["playerState"])
        if message.get("drcEnabled", False):
            writer.uint32(368).bool(message["drcEnabled"])
        if message.get("Jda", 0):
            writer.uint32(384).int32(message["Jda"])
        if message.get("qw", 0):
            writer.uint32(400).int32(message["qw"])
        if message.get("Ky", 0):
            writer.uint32(408).int32(message["Ky"])
        if message.get("sabrReportRequestCancellationInfo", 0):
            writer.uint32(432).int32(message["sabrReportRequestCancellationInfo"])
        if message.get("l", False):
            writer.uint32(448).bool(message["l"])
        if message.get("G7", 0):
            writer.uint32(456).int64(message["G7"])
        if message.get("preferVp9", False):
            writer.uint32(464).bool(message["preferVp9"])
        if message.get("qj", 0):
            writer.uint32(472).int32(message["qj"])
        if message.get("Hx", 0):
            writer.uint32(480).int32(message["Hx"])
        if message.get("isPrefetch", False):
            writer.uint32(488).bool(message["isPrefetch"])
        if message.get("sabrSupportQualityConstraints", 0):
            writer.uint32(496).int32(message["sabrSupportQualityConstraints"])
        if message.get("sabrLicenseConstraint", b''):
            writer.uint32(506).bytes(message["sabrLicenseConstraint"])
        if message.get("allowProximaLiveLatency", 0):
            writer.uint32(512).int32(message["allowProximaLiveLatency"])
        if message.get("sabrForceProxima", 0):
            writer.uint32(528).int32(message["sabrForceProxima"])
        if message.get("Tqb", 0):
            writer.uint32(536).int32(message["Tqb"])
        if message.get("sabrForceMaxNetworkInterruptionDurationMs", 0):
            writer.uint32(544).int64(message["sabrForceMaxNetworkInterruptionDurationMs"])
        if message.get("audioTrackId", ""):
            writer.uint32(554).string(message["audioTrackId"])

        return writer

    @staticmethod
    def decode(input_data, length=None):
        reader = input_data if isinstance(input_data, BinaryReader) else BinaryReader(input_data)
        end = reader.len if length is None else reader.pos + length
        message = ClientAbrState.create_base_client_abr_state()

        while reader.pos < end:
            tag = reader.uint32()
            field_number = tag >> 3

            if field_number == 13 and tag == 104:
                message['timeSinceLastManualFormatSelectionMs'] = long_to_number(reader.int64())
                continue
            elif field_number == 14 and tag == 112:
                message['lastManualDirection'] = reader.sint32()
                continue
            elif field_number == 16 and tag == 128:
                message['lastManualSelectedResolution'] = reader.int32()
                continue
            elif field_number == 17 and tag == 136:
                message['detailedNetworkType'] = reader.int32()
                continue
            elif field_number == 18 and tag == 144:
                message['clientViewportWidth'] = reader.int32()
                continue
            elif field_number == 19 and tag == 152:
                message['clientViewportHeight'] = reader.int32()
                continue
            elif field_number == 20 and tag == 160:
                message['clientBitrateCapBytesPerSec'] = long_to_number(reader.int64())
                continue
            elif field_number == 21 and tag == 168:
                message['stickyResolution'] = reader.int32()
                continue
            elif field_number == 22 and tag == 176:
                message['clientViewportIsFlexible'] = reader.bool()
                continue
            elif field_number == 23 and tag == 184:
                message['bandwidthEstimate'] = long_to_number(reader.int64())
                continue
            elif field_number == 24 and tag == 192:
                message['minAudioQuality'] = reader.int32()
                continue
            elif field_number == 25 and tag == 200:
                message['maxAudioQuality'] = reader.int32()
                continue
            elif field_number == 26 and tag == 208:
                message['videoQualitySetting'] = reader.int32()
                continue
            elif field_number == 27 and tag == 216:
                message['audioRoute'] = reader.int32()
                continue
            elif field_number == 28 and tag == 224:
                message['playerTimeMs'] = long_to_number(reader.int64())
                continue
            elif field_number == 29 and tag == 232:
                message['timeSinceLastSeek'] = long_to_number(reader.int64())
                continue
            elif field_number == 30 and tag == 240:
                message['dataSaverMode'] = reader.bool()
                continue
            elif field_number == 32 and tag == 256:
                message['networkMeteredState'] = reader.int32()
                continue
            elif field_number == 34 and tag == 272:
                message['visibility'] = reader.int32()
                continue
            elif field_number == 35 and tag == 285:
                message['playbackRate'] = reader.float()
                continue
            elif field_number == 36 and tag == 288:
                message['elapsedWallTimeMs'] = long_to_number(reader.int64())
                continue
            elif field_number == 38 and tag == 306:
                message['mediaCapabilities'] = reader.bytes()
                continue
            elif field_number == 39 and tag == 312:
                message['timeSinceLastActionMs'] = long_to_number(reader.int64())
                continue
            elif field_number == 40 and tag == 320:
                message['enabledTrackTypesBitfield'] = reader.int32()
                continue
            elif field_number == 43 and tag == 344:
                message['maxPacingRate'] = reader.int32()
                continue
            elif field_number == 44 and tag == 352:
                message['playerState'] = long_to_number(reader.int64())
                continue
            elif field_number == 46 and tag == 368:
                message['drcEnabled'] = reader.bool()
                continue
            elif field_number == 48 and tag == 384:
                message['Jda'] = reader.int32()
                continue
            elif field_number == 50 and tag == 400:
                message['qw'] = reader.int32()
                continue
            elif field_number == 51 and tag == 408:
                message['Ky'] = reader.int32()
                continue
            elif field_number == 54 and tag == 432:
                message['sabrReportRequestCancellationInfo'] = reader.int32()
                continue
            elif field_number == 56 and tag == 448:
                message['l'] = reader.bool()
                continue
            elif field_number == 57 and tag == 456:
                message['G7'] = long_to_number(reader.int64())
                continue
            elif field_number == 58 and tag == 464:
                message['preferVp9'] = reader.bool()
                continue
            elif field_number == 59 and tag == 472:
                message['qj'] = reader.int32()
                continue
            elif field_number == 60 and tag == 480:
                message['Hx'] = reader.int32()
                continue
            elif field_number == 61 and tag == 488:
                message['isPrefetch'] = reader.bool()
                continue
            elif field_number == 62 and tag == 496:
                message['sabrSupportQualityConstraints'] = reader.int32()
                continue
            elif field_number == 63 and tag == 506:
                message['sabrLicenseConstraint'] = reader.bytes()
                continue
            elif field_number == 64 and tag == 512:
                message['allowProximaLiveLatency'] = reader.int32()
                continue
            elif field_number == 66 and tag == 528:
                message['sabrForceProxima'] = reader.int32()
                continue
            elif field_number == 67 and tag == 536:
                message['Tqb'] = reader.int32()
                continue
            elif field_number == 68 and tag == 544:
                message['sabrForceMaxNetworkInterruptionDurationMs'] = long_to_number(reader.int64())
                continue
            elif field_number == 69 and tag == 554:
                message['audioTrackId'] = reader.string()
                continue
            else:
                if (tag & 7) == 4 or tag == 0:
                    break
                reader.skip(tag & 7)

        return message

def long_to_number(int64_value):
    value = int(str(int64_value))
    if value > (2 ** 53 - 1):
        raise OverflowError("Value is larger than 9007199254740991")
    if value < -(2 ** 53 - 1):
        raise OverflowError("Value is smaller than -9007199254740991")
    return value